.net 垃圾回收学习【Memory leak via event handlers】【翻译&&学习】
2011-08-27 18:07 一一九九 阅读(255) 评论(0) 编辑 收藏 举报From: http://blogs.msdn.com/b/abhinaba/archive/2009/05/05/memory-leak-via-event-handlers.aspx
分享一下我们的客户最近比较感兴趣的内存溢出的问题。
The short version
问题的核心是他们有Event sinks和Sources。 Event sinks被正确的释放,然而,在释放的过程中他们忘记了从触发事件的source中移除event sink.所以,事实上,已经被释放掉的sinks仍然可以从event source被reachable。所以GC不会释放Event sinks并且会导致泄漏的对象。
Now the not-so-long version
考虑如下代码,这里有EventSource和EventSink对象,EventSource对外公开SomeEvent事件,Sink可以订阅此事件。
EventSink sink = new EventSink();
EventSource src = new EventSource();
src.SomeEvent += sink.EventHandler;
src.DoWork();
sink = null;
// Force collection
GC.Collect();
GC.WaitForPendingFinalizers();
在以上的代码中假如你为EventSink添加一个Finaizer方法的话,然后在Finalizer方法中添加一个端点或者控制台输出的话,你会发现它永远不会触发,即使你将Sink设置为NULL。原因是在第三行我们将sink添加到了Source的Invoke list上了。所以即使sink被设置为NULL,最初的对象依然是可以从Src reachable的。
+=操作执行的时候将sink添加到了对象的触发列表中,当把sink设置为NULL的时候,Sink的老对象仍然在内存中,导致内存不停的增大,最终在某个点会导致崩溃。
修复的方法是确保移除了sink.
EventSink sink = new EventSink();
EventSource src = new EventSource();
src.SomeEvent += sink.EventHandler;
src.DoWork();
src.SomeEvent -= sink.EventHandler;
sink = null;
上面的代码只是一个示例,显然你不应该在多处进行Event的Add,remove操作。确保EventSink实现了IDisposable模式,将+=,-=操作封装在ctor/dispose中。