代码之家  ›  专栏  ›  技术社区  ›  mr.b Scott Lystig Fritchie

Dispose()方法中gc.SuppressFinalize(this)的目的是什么?

  •  28
  • mr.b Scott Lystig Fritchie  · 技术社区  · 14 年前

    我有以下代码:

    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 街区?

    在什么用例场景中,这将起到重要作用?

    5 回复  |  直到 6 年前
        1
  •  33
  •   Dirk Vollmar    14 年前

    在实现Dispose模式时,还可以向调用 Dispose() . 这是为了确保 处置() 总是 被调用,即使客户忘记调用它。

    要防止Dispose方法运行两次(如果对象已被释放),请添加 GC.SuppressFinalize(this); . 文档提供了 sample :

    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;         
        }
    }
    
        2
  •  30
  •   Carl Reinke Václav Dajbych    8 年前

    垃圾收集 :gc在不再引用对象时回收对象使用的内存。

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

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

    管理资源 :实现 IDisposable 接口,如流和数据库连接。

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


    现在回答你的问题:

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

    下次运行GC时,它将发现先前在可执行队列中的一些对象已经完成,因此可以进行回收。请注意,GC至少需要两个周期(如果要完成大量的定稿工作,则需要更多周期)来使用定稿器除去对象,这会导致一些性能损失。

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


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


    进一步阅读 :

        3
  •  6
  •   Thomas Pornin    14 年前

    可在第一次GC运行后完成的对象。

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

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

        4
  •  2
  •   Jesse C. Slicer    14 年前

    如果类型实现终结器( ~MyType() { } )它防止垃圾收集器运行它。当终结器处理非托管类型,但用户已调用 Dispose() (明示或通过 using() { } 块),释放这些非托管类型。

        5
  •  0
  •   Craig Trader    14 年前

    来自MSDN: GC.SuppressFinalize :

    这个方法在对象中设置了一点 标题,系统检查何时 正在调用终结器。obj参数 必须是此的调用方 方法。

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

    通常,如果对象不引用其他对象,只引用离散类型,或者已经将任何对象引用重置为空,则可以使用此方法。