前言:我知道如何解决问题。我想知道它为什么会出现。请从上到下阅读问题。
我们都(应该)知道,添加事件处理程序会导致 C# 中的内存泄漏。请参阅为什么以及如何避免事件处理程序内存泄漏?
另一方面,对象通常具有相似或相互关联的生命周期,并且没有必要取消注册事件处理程序。考虑这个例子:
using System;
public class A
{
private readonly B b;
public A(B b)
{
this.b = b;
b.BEvent += b_BEvent;
}
private void b_BEvent(object sender, EventArgs e)
{
// NoOp
}
public event EventHandler AEvent;
}
public class B
{
private readonly A a;
public B()
{
a = new A(this);
a.AEvent += a_AEvent;
}
private void a_AEvent(object sender, EventArgs e)
{
// NoOp
}
public event EventHandler BEvent;
}
internal class Program
{
private static void Main(string[] args)
{
B b = new B();
WeakReference weakReference = new WeakReference(b);
b = null;
GC.Collect();
GC.WaitForPendingFinalizers();
bool stillAlive = weakReference.IsAlive; // == false
}
}
A
并B
通过事件隐式地相互引用,但 GC 可以删除它们(因为它不使用引用计数,而是使用标记和清除)。
但现在考虑这个类似的例子:
using System;
using System.Timers;
public class C
{
private readonly Timer timer;
public C()
{
timer = new Timer(1000);
timer.Elapsed += timer_Elapsed;
timer.Start(); // (*)
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
// NoOp
}
}
internal class Program
{
private static void Main(string[] args)
{
C c = new C();
WeakReference weakReference = new WeakReference(c);
c = null;
GC.Collect();
GC.WaitForPendingFinalizers();
bool stillAlive = weakReference.IsAlive; // == true !
}
}
为什么GC不能删除C
对象?为什么 Timer 使对象保持活动状态?计时器是否通过计时器机制的一些“隐藏”参考(例如静态参考)保持活动状态?
(*) 注意:如果只创建计时器,而不启动计时器,则不会出现此问题。如果它已启动然后停止,但事件处理程序未取消注册,则问题仍然存在。