剖析WorkflowFoundation的内存泄漏问题
最近项目在使用微软的WorkflowFoundation框架进行流程开发,在使用WorkflowDesigner的时候,会发生内存和句柄泄漏的现象。开始泄漏分析:
句柄泄漏使用微软的 handle.exe,https://technet.microsoft.com/en-us/sysinternals/bb896655.aspx,将handle.exe放到c:\\windows\system32下。快捷键windows+r运行cmd命令,输入handle -p [processname] -s;发现etwregistration句柄在不断增长,经过对源码进行分析,发现在调用WorkflowDesigner的Load(object obj)方法时,创建了EventProvider,继承了IDisposable接口。在销毁所有的服务的时候并没有对这个非托管资源进行销毁,进而导致句柄泄漏。这里说一句,windows程序都会有一个最大句柄数,一但超过这个最大数,应用就会报错。由于EventProvider是被包装成private的成员变量,所以只能通过反射的方式去释放资源。
void CloseDesigner(WorkflowDesigner designer)
{
if (designer == null)
return;
var service = designer.Context.Services;
var serviceType = service.GetType();
var subservice = serviceType.GetField("_subscriptions",System.Reflection.BindingFlags.IgnoreCase| System.Reflection.BindingFlags.Instance| System.Reflection.BindingFlags.NonPublic);
if (subservice != null)
{
var subserviceFiled= subservice.GetValue(service);
var kv = subserviceFiled as Dictionary<Type, SubscribeServiceCallback>;
if (kv != null)
{
Dictionary<Type, SubscribeServiceCallback> cache = new Dictionary<Type, SubscribeServiceCallback>();
foreach (var item in kv)
{
cache.Add(item.Key,item
.Value);
}
foreach (var item in cache)
{
service.Unsubscribe(item.Key, item.Value);
}
cache.Clear();
}
}
var services = serviceType.GetField("_services", System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (services != null)
{
var servicesField = services.GetValue(service);
var kv = servicesField as Dictionary<Type, object>;
if (kv != null)
{
foreach (var item in kv)
{
var keyType = item.Key;
if (keyType.Name == "DesignerPerfEventProvider")
{
var instance = item.Value;
var provider = keyType.GetField("provider", System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (provider != null)
{
var providerField= provider.GetValue(instance);
EventProvider eventProvider = providerField as EventProvider;
eventProvider?.Dispose();
eventProvider = null;
}
break;
}
}
}
}
designer.Context.Dispose();
designer.ContextMenu?.Items.Clear();
designer.View?.CommandBindings?.Clear();
designer = null;
}
分析应用内存,使用vs的工具快照进行分析,经过分析比对。DesignerView这个对象的引用一直没有得到释放,经过对源码进行剖析。发现内部有大量的委托事件未得到释放,这是一个挺巨大的工程,释放了部分事件后,泄漏有所减轻,要想彻底的解决还需要在花精力进行分析。暂时只能先解决到这个层面,希望感兴趣的同学能够对这个问题进行深入研究。也希望微软能解决这个bug。