生活、程序、健身程序员

剖析WorkflowFoundation的内存泄漏问题

2019-12-30  本文已影响0人  Statham_Jessie

        最近项目在使用微软的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。

上一篇下一篇

猜你喜欢

热点阅读