事件处理程序很容易导致内存泄漏,因为事件的调用列表保存了对事件处理实例的引用,所以如果事件源仍然存在,则无法回收事件处理实例。
但请考虑以下代码:
public class SomeClass
{
public event EventHandler SomeEvent;
}
public class MyClass
{
public MyClass(SomeClass source)
{
//VERSION 1
source.SomeEvent += OnSomeEvent;
//VERSION 2
void localHandler(object s, EventArgs args) { Console.WriteLine("some action with(out) any references"); }
source.SomeEvent += localHandler;
//VERSION 3
var x = new object();
source.SomeEvent += (s, e) => { Console.WriteLine("some event fired, using closure x:" + x.GetHashCode()); };
//VERSION 4
source.SomeEvent += (s, e) => { Console.WriteLine("some action without any references"); };
}
private void OnSomeEvent(object sender, EventArgs e)
{
//...
}
}
以下是我对不同事件处理版本可能导致内存泄漏的假设/问题:
- 版本1:因为调用目标明确引用了
MyClass
的实例。 - 版本2:因为对
localHandler
的引用意味着对MyClass
实例的引用——除非localHandler
内部的代码没有引用MyClass
的实例? - 版本3:因为lambda包含一个闭包,它本身是对
MyClass
实例的引用——或者不是吗? - 版本4:因为lambda没有引用
MyClass
实例,所以这可能不会导致泄漏?
关于版本3和4的后续问题:
- "魔术帮助对象"在哪里存储,.Net为lambda/闭包创建它,并且它是否(总是)包含一个引用,可以使
MyClass
实例保持活动状态? - 如果lambda事件处理程序可能会泄漏,那么它们只能在不会出现问题的情况下使用(例如,
MyClass
实例比SomeClass
实例更长寿),因为它们不能使用-=
进行删除?
编辑:此帖子(原标题为“何时会导致事件处理程序内存泄漏?”)被建议作为重复项,链接为Why and How to avoid Event Handler memory leaks?,但我不同意,因为该问题是针对lambda事件处理程序的。我重新表述了问题/标题,以使这一点更加清晰。