事件处理程序影响 CLR 中的垃圾回收

Event handler affects in garbage collection in CLR

我对事件处理程序如何影响垃圾收集操作感到非常困惑。

例如,为什么对象a1没有被垃圾回收回收(a1的析构函数没有调用):

即使在取消订阅 timeChange 事件处理程序之后,垃圾收集器也不会调用析构函数。

此致。

public class B
{
    private void button1_Click(object sender, EventArgs e)
    {    
        A a1 = new A();
        a1.timeChange += A1_timeChange;

        a1.Start();

        a1 = null;

        GC.Collect();
    }

    private void A1_timeChange(object sender, EventArgs e)
    {
        MessageBox.Show(((DateTime)sender).ToString() );
    }
}

public class A
{
    ~A()
    {
        MessageBox.Show("A Collected");
    }

    public void Start()
    {
        if (timeChange != null)
        {
            Task.Factory.StartNew(() => {
                while (true)
                {
                    timeChange(DateTime.Now, null);
                    System.Threading.Thread.Sleep(3000);
                }
            });
        }
    }
    public event EventHandler timeChange;
}

总结
造成这种情况的不是事件本身,而是使用闭包从长 运行ning 线程引用 class A 的实例成员。

事件本身不是问题,是吗?
此代码 a1.timeChange += A1_timeChange; 导致 class A 中实现事件 public event EventHandler timeChange 的委托引用 class B 中的 A1_timeChange
所以,参考是另一种方式
在您的场景中,如果您摆脱了对 class B 的所有引用,但没有取消订阅该事件,那么 A 中指向 B 中的处理程序方法的事件处理程序可以保持 class B 可访问, 因此没有 GC'ed

到底发生了什么
您的 class A 可从 GC 根之一访问(具体来说,主动 运行ning 线程),它仍然可以访问,因此未被收集 - read more

您使用此代码

生成了一个永久 运行ning 任务(当应用程序关闭/前台线程终止时它将停止)
Task.Factory.StartNew(() => {
                while (true)
                {
                    timeChange(DateTime.Now, null);
                    System.Threading.Thread.Sleep(3000);
                }

事实是,您使用的是关闭 timeChange 事件的 lambda,这使编译器生成一个 class,它仅引用 class A

还有一件事,只要有一个析构函数(它被编译成一个终结器),你就可以通过一次 GC 收集来延长你的对象的生命周期——在第一次 GC 上,你的对象将被标记为不可访问,并且将放入终结队列。而在 next GC 上,finalizer 实际上会 运行 - read more