为什么继承自System.ComponentModel.Component会阻止对象被垃圾回收(完全不被回收)?

3
我的原始问题是,在继承自例如Component的类中,注销自引用事件处理程序是否重要。它们提供了一个Disposed事件,可以取消初始化内容。
但我进行测试时发现了一些奇怪的事情:从System.ComponentModel.Component派生会完全阻止析构函数被调用(即使在应用程序结束时也是如此)。
以下是一个样例,明确使用GC.Collect来强制收集(仅用于测试,永远不要在生产代码中使用)。
using System;
using System.ComponentModel;

namespace CSharpEventHandlerLifetime
{
    public class LongLiving : Component
    {

        public LongLiving()
        {
            Console.WriteLine("Creating object");
            // Disposed += (sender, args) => Console.WriteLine("Disposed");
        }

        ~LongLiving()
        {
            Console.WriteLine("Destructor called");
        }

    }

    class Program
    {
        public static void _create()
        {
            using (var l = new LongLiving())
            {
                Console.WriteLine("Generation is {0}", GC.GetGeneration(l));
            }
        }

        static void Main(string[] args)
        {
            _create();

            GC.Collect(); // this should call dtor of LongLiving


            Console.ReadKey(); // wait before end
        }
    }
}

当我完全移除继承的Component类(将using更改为普通的new)或者将其替换为IDisposable(并实现一些空的Dispose方法)时,我清楚地看到调用GC.Collect会调用LongLiving的dtor。

我不理解这种行为,因为我至少希望在应用程序退出时打断~dtor,但是当从Component派生时,它永远不会发生。


请注意,Finalizable对象不会在单个GC中被垃圾回收。它们会一直存活到下一次垃圾回收。 - Simon Whitehead
@SimonWhitehead - 的确如此,但它们之所以能够在第一次垃圾收集中幸存下来,正是因为它们的终结器已被调度。因此,如果它们幸存下来,并且它们是很少等待被终结的对象之一,那么即使终结器没有执行(在合理的时间范围内,终结器线程没有被阻塞等),这仍然会让人感到惊讶。 - Damien_The_Unbeliever
你能详细解释一下“不使用单个GC进行垃圾回收”的含义吗?如果应用程序存在,下一个垃圾回收是什么?但是,SuppressFinalize听起来更清晰。 - Samuel
@Damien 我的观点只是,由于你无法保证何时会发生.. 因此测试是有缺陷的。 - Simon Whitehead
@SimonWhitehead - 但是修复测试的方法是插入一个对WaitForPendingFinalizers的调用,而不仅仅是立即运行第二个收集器,这是你第一条评论所暗示的解决方法。 - Damien_The_Unbeliever
因此,“注意”而不是“这里有一个修复”。 :) - Simon Whitehead
1个回答

8

Component已经实现了可处理模式,其中包括在完成时调用GC.SuppressFinalize。当using块退出时,Dispose被调用。

因此,对象会被垃圾回收,但其终结器永远不会被调用。

如果您需要进行任何清理工作,应该重写Dispose(bool)方法。

例如,这是 ComponentDispose 方法:

public void Dispose()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接