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

Sam*_*ron 274 .net c# garbage-collection idisposable suppressfinalize

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

使用这种方法有什么好处?

Rob*_*son 286

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

通常,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
Run Code Online (Sandbox Code Playgroud)

  • 如果实现`IDisposable`的类不是`sealed`,那么它应该包括对`GC.SuppressFinalize(this)`*的调用,即使它不包含用户定义的终结器*.这对于确保添加用户定义的终结器但只覆盖受保护的`Dispose(bool)`方法的派生类型的正确语义是必要的. (28认同)
  • @Dreamer - 这取决于你的实现.通常,您想知道终结器是否正在调用Dispose而不是IDisposable.Dispose()实现.如果从终结器调用,您必须假定私有引用不再有效,并且您实际上做不了多少.如果从IDisposable.Dispose()调用,您知道引用仍然有效. (3认同)
  • 对于派生类来说,不被 @SamHarwell 提到的“密封”很重要。当类未密封时,CodeAnalysis 结果为 ca1816+ca1063,但密封类在没有“SuppressFinalize”的情况下也可以。 (3认同)
  • 在第一个代码片段中,我只是发布了推荐的 IDisposable + 终结器模式的样子。调试代码固然很好,但它可能会分散注意力。..我只能建议避免使用终结器,除非具有非托管资源的类。编写安全的终结器代码并非易事。 (2认同)
  • 嗨,为什么我们需要从终结器中以 false 作为参数调用 dispose?如果 dispose 从未被调用,然后它不会处理怎么办?如果我们只是检查对象是否已被处理并进行实际清理会怎样。 (2认同)

Mic*_*urr 37

你告诉系统,在终结器中完成的任何工作都已经完成,所以不需要调用终结器.从.NET文档:

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

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

SupressFinalize只是提供一种优化,允许系统不打扰将对象排队到终结器线程.无论是否调用GC.SupressFinalize(),正确编写的Dispose()/终结器都可以正常工作.

  • *无论是否调用 GC.SupressFinalize(),正确编写的 Dispose()/finalizer 都应该正常工作* - 出于兴趣,现在是否有任何理由建议建议使用“SupressFinalise”(2022/C# 10 +)? (3认同)

小智 6

Dispose(true);
GC.SuppressFinalize(this);
Run Code Online (Sandbox Code Playgroud)

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

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

因此调用GC.SuppressFinalize(this)删除终结队列中的引用。

  • IMO 这是一种反模式。充其量它属于过早优化的范围。最终确定队列是系统中的瓶颈吗?不太可能。这样做错误是否会产生性能问题,是的。所以“治愈”比疾病更糟糕。 (3认同)