32

我有以下代码:

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 相关的调用的目的,但我仍然不明白它为什么会在那里。

一旦所有实例不再存在,例如在using块中使用时,对象是否注定要进行垃圾收集?

这将发挥重要作用的用例场景是什么?

4

5 回答 5

35

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

为了防止 dispose 方法运行两次(如果对象已经被释放),请添加GC.SuppressFinalize(this);. 该文档提供了一个示例

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;         
    }
}
于 2010-06-14T15:42:43.267 回答
34

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

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

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

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

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


现在回答你的问题:

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

下次 GC 运行时,它会发现之前在 Freacheable 队列中的一些对象已经被 Finalized 准备好进行回收。请注意,GC 至少需要两个周期(如果有很多 Finalization 要做,则需要更多周期)才能使用 Finalizer 处理掉一个对象,这会导致一些性能损失。

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


话虽如此,您很少需要编写终结器,因为对于绝大多数常见的非托管资源,托管包装器已经存在,并且托管资源将通过Dispose从您自己的Dispose方法调用它们的方法来释放,只有从那里!在终结器中,您绝不能调用 Dispose 方法。


进一步阅读

于 2010-06-14T16:30:04.917 回答
6

可以完成的对象在第一次 GC 运行后仍然存在。

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

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

于 2010-06-14T15:46:12.057 回答
2

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

于 2010-06-14T15:42:12.753 回答
0

来自 MSDN:GC.SuppressFinalize

此方法在对象头中设置一个位,系统在调用终结器时会检查该位。obj 参数必须是该方法的调用者。

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

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

于 2010-06-14T15:43:28.537 回答