GC.SuppressFinalize对比非可终止对象的性能表现

11

具有GC.SuppressFinalize的可终止对象和普通不可终止对象是否相同?以下代码似乎证明它们在.NET 2和4上被不同对待:

class Class1 {

    public Class1()
    {
        GC.SuppressFinalize(this);
    }

    //~Class1() { }
}

class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();

        for (int i=0; i<100000000; i++)
        {
            new Class1();
        }

        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);
    }
}

仅添加 finalizer 而不更改其他内容会导致代码运行时间大大增加(从 889ms 增加到 12601ms)。

我之前以为在对象头部设置一个位来使 GC 将具有 finalizer 的对象视为非 finalizable 对象,但这并不是事实。那么出了什么问题?GC.SuppressFinalize 方法调用前后在不具有 finalizer 和具有 finalizer 的对象之间的区别是什么?


2
我也在 .NET 4.5 beta 上重现了这个问题。 - Robert Levy
顺便提一下,我重复了测试,但是计时了在所有的“new”之后执行GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();所需的时间。使用或不使用(被抑制的)终结器没有明显的差异。 - dlf
1个回答

8
据我了解,CLR有一个对象队列,用于注册终结器。实现终结器将把该类型的对象放入队列中。因此,在构造函数调用SuppressFinalize的情况下,我想象对象实际上被放入队列中,只是立即被移除,这可能解释了开销。

3
你的想象力非常准确 :) - Hans Passant
1
这看起来非常低效。一个不可终止的对象应该与一个抑制终止的对象完全相同。令人惊讶的是它们之间有所不同。 - thecoop
@thecoop:也许是这样,但任何实现终结器的类型在构造时都需要以不同的方式处理。我猜你可以进行优化,检查构造函数期间是否抑制了终结操作,但我认为这并不值得努力。在常见情况下,抑制发生在对象构造之后的某个时间。 - Brian Rasmussen
@Brian Rasmussen为什么需要在构造时特别处理可终结类型?终结只影响它的垃圾回收方式... - thecoop
1
@thecoop “一个不可终止的对象应该与一个被抑制的可终止对象完全相同。” - 不是这样的。试着找一下相关的参考资料,你会发现没有。明确声明,在实例化时添加析构函数会增加一些开销。 - H H
显示剩余3条评论

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