事件(Event),绝大多数内存泄漏(Memory Leak)的元凶[上篇]
最近这两天一直在忙着为一个项目检查内存泄漏(Memory Leak)的问题,对相关的知识进行了一下简单的学习和探索,其间也有了一些粗浅的经验积累,今天特意写一篇相关的文章与大家分享。那些对内存泄漏稍微有点了解的人,对于本篇文章的标题,相信不会觉得是在危言耸听。就我查阅的资料,已经这两天的发现也证实了这一点:觉得部分的内存泄漏问题与事件(Event)有关。本篇文章将会介绍其原理,以及如何发现和解决由事件导致的内存泄漏问题。 首先定义表示每一项TotoList Item定义了一个相应的类型:Event(不是我们谈到的导致内存泄漏的事件)。Event仅仅包含简单的属性:主题(Subject),截至日期(DueDate)和相应的描述性文字(Description),Event定义如下: 1: using System; 3: { 5: { 7: public DateTime DueDate { get; set; }
9: public Event(string subject,DateTime dueDate,string desc) 11: if (string.IsNullOrEmpty(subject)) 13: throw new ArgumentNullException("subject"); 15: this.Subject = subject;
17: this.Description = desc ?? string.Empty; 19: }
整个应用就这么简单,但是为了确定是否真的出现内存泄漏,我们需要在查看内存状态的时候,确保GC把所有垃圾对象全部回收完毕。为此,我在整个应用级别定义了一个静态的System.Threading.Timer,让它每隔半秒调用一次GC.Collect()。 class Program
8: (state => GC.Collect(),500); void Main() 12: Application.EnableVisualStyles(); 14: Application.Run(new MainForm());
17: } 接下来我查看我们的应用程序是否会有内存泄漏的问题了。查看内存泄漏,当然不能通过我们的肉眼去捕捉,需要借助响应的Memory Profiling工具。我们有很多这样的工具,有免费的,也有需要付钱购买的。在这里我推荐两个Memory Profiling工具,一个是JetBrains的dotTrace,另一个是RedGate的ANTS Memory Profiler,前者是免费的,后者不是。在这里我通过后者来查看本应用的内存泄漏问题。 ANTS Memory Profiler通过这样的原理来确定你的应用程序是否有泄漏问题:如果你怀疑某个操作会导致应该被GC回收的对象没有被回收,那么你在之前对内存分配情况拍一张快照(Snapshot),然后执行该操作,在操作完成并确定GC完成相应的回收操作后,在拍一张快照。通过对比,找出多余的对象,并根据具体的情况分析该对象是否应该被GC回收,如果是的,怎意味着你的程序存在着内存泄漏问题。关于ANTS Memory Profiler的具体操作,这里就不再细说了,只要大家了解基本的原理,不影响对后面内容的理解就可以了。 左图就是TodoListForm对象在内存中的引用链,我们可以很清楚地看到:该对象被TodoListManager的一个类型为EventHandler<TodoListEventArgs>的事件引用,这个对象实际上是一个Delegate对象,而TodoListForm作为这个Delegate对象的Target。通过上面给出的代码,我们不难想出是由于在TodoListForm实现了对TodoListManager的TotoListChanged事件注册导致了TodoListManager不能被垃圾回收。 上面的实力说明了这么一种情况:对于GUI应用可视化树形结构来说,一个窗体被关闭,照例说它应该成为垃圾对象,GC在执行垃圾回收的时候就可以将其清楚的。但是,由于该对象注册了一个事件到一个生命周期很长的对象(在本例中,TodoManager是一个Singletone对象,具有和整个应用程序一样的生命周期),它就是被这么一个对象长期引用,进而阻止 GC对其的回收工作。 所以,在这种情况:短暂生命周期注册事件到长期生命周期对象上,在该对象被Dispose的时候,应该解除事件的注册。你可以通过实现System.IDisposable接口,将解除事件注册的操作放在Dispose方法中。对于本里来说,你可以将相应的操作注册到Form的Closing、Closed或者Disposed事件中。比如在下面代码中,我为TodoListForm添加了如下一个Closing事件处理程序: 13: state => 15: BindingSource bindingSource = new BindingSource();
this.dataGridViewTodoList.DataSource = bindingSource; 19: } 21: void TodoListForm_FormClosing( 22: { 24: } 26: } ? 那么,在此按照上面的流程利用ANTS Memory Profiler查看内存泄漏,在第二个快照中,你将再也看不到TodoListForm的身影(如下图)。本篇主要介绍如何重现事件注册导致内存泄露,已及最直接的解决方案。下一篇我将进一步对其背后的原理进行剖析,并提出另一种更加“优雅而可靠”解决方案。 作者:Artech
出处:http://artech.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – 清除Kendo Validator错误消息
- asp.net – Sitecore:按字段选择项目:Treelist
- asp.net – 如何在回发中保存asp:HiddenField值
- asp.net – 如何在不构建网站的情况下使用MSBuil
- asp.net-mvc – ASP.NET MVC中Controller.ReadFr
- asp.net-mvc – Jasmine在一个单独的测试项目中
- 如何在ASP.NET中将文本框的宽度设置为与MaxLengt
- msbuild – 如果不指定目标框架,则不支持“发布”
- asp.net – 自我跟踪实体vs POCO实体
- asp.net-mvc – 在数组中使用jqAutocomplete