什么时候应该使用GC.SuppressFinalize()?


287

在.NET中,我应在哪种情况下使用GC.SuppressFinalize()

使用此方法有什么优点?


我已经看到了一些关于终结器和IDisposable的问题,stackoverflow也应该包含有关GC.SupressFinalize和弱引用的信息
Sam Saffron

我认为弱引用对终结器没有任何作用-也许您应该发布有关终结器的更直接问题。
迈克尔·伯

是的,我的意思是要发布一个关于弱引用的单独问题,当您构建对象池时,所有这些都可以联系在一起。我也应该问一个关于对象复兴的问题ala ReRegisterForFinalize
Sam Saffron

Answers:


296

SuppressFinalize只能由具有终结器的类调用。它通知垃圾收集器(GC)this对象已被完全清除。

IDisposable拥有终结器的推荐模式是:

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose(). 
                // OK to use any private object references
            }
            // Release unmanaged resources.
            // Set large fields to null.                
            disposed = true;
        }
    }

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

    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}

通常,CLR在创建终结器时保留带有终结器的对象的选项卡(使它们的创建成本更高)。 SuppressFinalize告诉GC该对象已正确清理,不需要进入终结器队列。它看起来像一个C ++析构函数,但功能却不一样。

SuppressFinalize优化并非易事,因为您的对象可以在终结器队列上等待很长时间。不要试图让SuppressFinalize其他对象引起您的注意。那是一个严重的缺陷,等待发生。

设计准则通知我们,如果您的对象实现了IDisposable,则无需终结器,但是如果您有终结器,则应实现IDisposable以允许对类进行确定性清理。

大多数时候,您应该能够IDisposable清理资源。仅当您的对象保留非托管资源时,才需要终结器,并且需要确保这些资源被清除。

注意:有时,编码人员会添加一个终结器来调试其自己的IDisposable类的生成,以测试代码是否IDisposable正确处理了其对象。

public void Dispose() // Implement IDisposable
{
    Dispose(true);
#if DEBUG
    GC.SuppressFinalize(this);
#endif
}

#if DEBUG
~MyClass() // the finalizer
{
    Dispose(false);
}
#endif

1
在第一个代码段中,我只是发布推荐的IDisposable + finalizer模式的样子。调试代码是好的,但可能会分散注意力。..我只能建议避免使用终结器,但具有非托管资源的类除外。编写安全的终结器代码并非易事。
罗伯特·保尔森

1
嗨,我们为什么需要从终结器中调用以false作为参数的dispose?如果处置从未被调用却又不会处置怎么办?如果我们仅检查对象是否已处置并进行实际清理,该怎么办?
Dreamer 2012年

3
@Dreamer-这取决于您的实现。通常,您想知道终结器与IDisposable.Dispose()实现是否正在调用Dispose。如果从终结器调用,则必须假定私有引用不再有效,并且您实际上不能做很多事情。但是,如果从IDisposable.Dispose()进行调用,则您知道引用仍然有效。
罗伯特·保尔森

32
如果该类的实现IDisposable不是sealed,则GC.SuppressFinalize(this) 即使它不包括用户定义的finalizer,它也应包括对的调用。这对于确保添加了用户定义的终结器但仅覆盖受保护的Dispose(bool)方法的派生类型具有适当的语义是必要的。
Sam Harwell

1
不被sealed通过@SamHarwell提到的是非常重要的,派生类。当类未密封时,CodeAnalysis会生成ca1816 + ca1063,但没有时,密封类很好SuppressFinalize
破旧的

38

SupressFinalize告诉系统在终结器中将完成的所有工作都已经完成,因此不需要调用终结器。从.NET文档:

实现IDisposable接口的对象可以从IDisposable.Dispose方法调用此方法,以防止垃圾回收器在不需要它的对象上调用Object.Finalize。

通常,大多数任何Dispose()方法都应该能够调用GC.SupressFinalize(),因为它应该清除将在终结器中清除的所有内容。

SupressFinalize只是提供了一种优化,使系统不必费心将对象排队到终结器线程。编写正确的Dispose()/ finalizer应该在调用或不调用的情况下正常工作GC.SupressFinalize()


2

必须在Dispose实现的对象的方法上调用该方法,IDisposable这样,如果有人调用该Dispose方法,GC将不会再次调用终结器。

请参阅:GC.SuppressFinalize(Object)方法-Microsoft Docs


9
我认为“必须”是错误的-甚至没有“应该”-只是在某些情况下,您可以消除排队/完成对象的开销。
基本

1
Dispose(true);
GC.SuppressFinalize(this);

如果对象具有终结器,.net会将引用放入终结队列中。

既然有了call Dispose(ture),它清除了对象,所以我们不需要终结队列来完成这项工作。

因此,GC.SuppressFinalize(this)在完成队列中调用remove reference。


0

如果一个类或其派生类中的任何一个都可以使用终结器保存对对象的最后一个实时引用,则选择 GC.SuppressFinalize(this)GC.KeepAlive(this)可能受到该终结器不利影响的任何操作之后,应在该对象上调用或应该对该对象进行调用,从而确保终结器获胜在该操作完成后才能运行。

在没有终结器的任何类中,GC.KeepAlive()和的成本GC.SuppressFinalize(this)基本相同,并且具有终结器的类通常应调用GC.SuppressFinalize(this),因此Dispose()不一定总是需要使用后者作为最后一步,但这不会是错的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.