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

时间:2017-12-09 14:30:59

标签: c# .net winforms garbage-collection eventhandler

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

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

即使在取消订阅timeChange eventHandler之后,析构函数也不会被垃圾收集器调用。

最好的问候。

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;
}

1 个答案:

答案 0 :(得分:1)

总结
事件本身不是导致这种情况,而是使用闭包来从长时间运行的线程引用类A的实例成员。

事件本身不是问题,或者是它?
此代码a1.timeChange += A1_timeChange;导致类A中的委托实现事件public event EventHandler timeChange以引用类B中的A1_timeChange。 因此,引用是其他方式
在你的场景中,如果你摆脱了对B类的所有引用,但没有取消订阅该事件,那么A中指向B中的处理程序方法的事件处理程序可以保持B类可达,因此不是GC'编

真实情况
您的A类可以从其中一个GC根(特别是主动运行的线程)访问,它仍然可以访问,因此不会被收集 - enter image description here

使用此代码生成了一个永久运行的任务(当应用程序关闭/前台线程终止时它会停止)

Task.Factory.StartNew(() => {
                while (true)
                {
                    timeChange(DateTime.Now, null);
                    System.Threading.Thread.Sleep(3000);
                }

问题是,您正在使用关闭timeChange事件的lambda,这使得编译器生成一个只引用A类的类

还有一件事,就是通过一个descructor(编译成终结器),你可以通过一个GC集合延长对象的生命周期 - 在第一个GC上,你的对象将被标记为无法访问,并将被放到终结队列。在 next GC上,终结器将实际运行 - read more