C# 什么';Dispose()方法中GC.SuppressFinalize(此)的用途是什么?

C# 什么';Dispose()方法中GC.SuppressFinalize(此)的用途是什么?,c#,.net,garbage-collection,idisposable,suppressfinalize,C#,.net,Garbage Collection,Idisposable,Suppressfinalize,我有以下代码: public void Dispose() { if (_instance != null) { _instance = null; // Call GC.SupressFinalize to take this object off the finalization // queue and prevent finalization code for this object from // exec

我有以下代码:

public void Dispose()
{
    if (_instance != null)
    {
        _instance = null;
        // Call GC.SupressFinalize to take this object off the finalization
        // queue and prevent finalization code for this object from
        // executing a second time.
        GC.SuppressFinalize(this);
    }
}
尽管有一条注释解释了那个次GC相关调用的目的,但我仍然不明白为什么会有这样的注释

当所有实例都停止存在时,对象不是注定要进行垃圾收集吗,比如在
使用
块时


这将在什么用例场景中发挥重要作用?

如果您的类型实现了终结器(
~MyType(){}
),它将阻止垃圾收集器运行它。当终结器处理非托管类型,但用户已经调用了
Dispose()
(可以显式调用,也可以通过
使用(){}
块调用),释放这些非托管类型时使用。

在实现Dispose模式时,还可以将终结器添加到调用
Dispose()
的类中。这是为了确保总是调用
Dispose()
,即使客户端忘记调用它

为了防止dispose方法运行两次(如果对象已经被释放),可以添加
GC.SuppressFinalize(此)。该文件提供了一个:

来自MSDN::

此方法在对象中设置一个位 标题,系统将在 调用终结器。obj参数 必须是此的调用方 方法

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


通常,如果您的对象不引用其他对象,而只是离散类型,或者已经将任何对象引用重置为NULL,则会使用此选项。

可以在第一次GC运行后最终确定的对象

通常,当GC检测到某个对象不可访问时,它会回收该对象。如果对象是可终结的,那么GC不会回收它;相反,它认为它仍然是可访问的(以及该对象引用的所有对象,等等),并安排它完成。只有在最终确定后的某个时间点再次发现无法访问该对象时,才会回收该对象


这意味着一个可终结的对象会产生额外的成本:该对象必须在内存中保留更长的时间。因此,您看到了这样一个调用:在不需要终结时,抑制它是值得的。在这里,对象使用终结来确保它总是在某个时刻被“处置”。当它被显式释放时,就不需要再对其进行最终处理。

垃圾收集:当对象不再被引用时,GC会回收对象使用的内存

Dispose:IDisposable接口中的一种方法,当程序员调用它时(直接或通过using块间接调用)释放所有托管和非托管资源

终结器:释放所有非托管资源的方法。在回收内存之前由GC调用

托管资源:实现
IDisposable
接口的任何.NET类,如Streams和DbConnections

非托管资源:封装在托管资源类中的填充。Windows句柄是最简单的例子


现在回答你的问题:

GC保留所有对象的列表(终结队列),这些对象的类声明了终结器(~ClassName in C#)。对象在创建时放入此队列。GC会定期运行,以检查是否存在程序无法访问的对象。然后,它检查是否有任何不可访问的对象从终结队列中引用,并将这些对象放入另一个称为Freacheable队列的队列中,而其余对象则被回收。单独的线程用于运行Freacheable队列中对象的Finalize方法

下次GC运行时,它会发现以前在Freacheable队列中的一些对象已经完成,因此可以进行回收。请注意,GC至少需要两个周期(如果有很多终结器要做,则需要更多周期)才能使用终结器除去对象,这会导致一些性能损失

SuppressFinalize
方法只是在对象头中设置一个标志,指示不必运行终结器。这样GC就可以立即回收对象的内存。根据上面的定义,
Dispose
方法与Finalizer(以及更多)做相同的事情,因此如果它被执行,那么Finalization就不再是必需的了。使用
SuppressFinalize
方法,您可以通过通知GC这个事实来为它保存一些工作。此外,现在您不必在终结器中实现检查以避免双重释放。
Dispose
的唯一问题是它不能保证运行,因为调用它是程序员的责任,这就是为什么有时我们需要使用终结器


也就是说,您很少需要编写终结器,因为对于大多数常见的非托管资源来说,托管包装器已经存在,并且托管资源将通过从您自己的
Dispose
方法调用它们的
Dispose
方法来释放,并且仅从那里开始!在终结器中,决不能调用Dispose方法


进一步阅读


0xA3的答案要好一点,因为您也可以在该场景中处理托管类型……没有什么可以阻止您尝试在Finalizer中处理托管类型,但这并不是一个好主意。最终确定顺序不是确定的,因此子对象可能在其父对象之前已被清除,等等。您可以添加一个终结器。但以我的经验来看,很少。在很多情况下,即时通讯是有意义的
class MyResource : IDisposable
{
    [...]

    // This destructor will run only if the Dispose method 
    // does not get called.
    ~MyResource()      
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue 
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            // If disposing equals true, dispose all managed 
            // and unmanaged resources.
            if(disposing)
            {
                // Dispose managed resources.
                component.Dispose();
            }

            // Call the appropriate methods to clean up 
            // unmanaged resources here.
            resource.Cleanup()          
        }
        disposed = true;         
    }
}