代码之家  ›  专栏  ›  技术社区  ›  CodeMonkey

在终结器中释放的资源与在Dispose中释放的资源有什么区别

  •  1
  • CodeMonkey  · 技术社区  · 6 年前

    这是这个问题的后续问题:

    Finalize/Dispose pattern in C#

    所以我理解如果我创建一个使用非托管资源的类,我应该处理它们。链接问题中的答案表示终结器处理非托管资源。然而, Dispose(Boolean) 方法还处理非托管资源:

    protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // get rid of managed resources
            }   
            // get rid of unmanaged resources
        } 
    

    那么,定型机的处置和处置方法的处置之间有什么区别呢?

    4 回复  |  直到 6 年前
        1
  •  3
  •   TheGeneral    6 年前

    你使用它的唯一原因(及其极具争议性)。

    1. 终结器允许在垃圾收集器删除对象之前清除该对象。(也就是说,GC负责调用它,并从内存中清除对象)如果开发人员忘记调用 Dispose() 方法,则可以释放非托管资源,从而避免泄漏。

    不这样做有很多原因,也有很多方法会出错。简言之,你很少有理由需要这么做,或者想这么做。

        2
  •  1
  •   Michał Turczyn    6 年前

    除了给出的答案:终结器在运行时由垃圾收集器调用。

    所以您不能依赖于在终结器中释放非托管资源的时间!因为它是未知的。

    另外,终结器在另一个线程上运行,因此垃圾收集完成后,终结可能仍在运行!所以必须通过另一个垃圾收集来完全除去一个对象。

    因此,第一个垃圾收集调用finalezrs,但对象未被收集(以及对象持有引用的对象),它将在第二个垃圾收集中被收集。

        3
  •  0
  •   Zdeněk Jelínek    6 年前

    带有终结器的对象经历两个GC阶段:第一次运行终结器,第二次实际收集对象并释放内存。除了增加GC压力和延迟内存释放回池之外,终结器还具有处理字段可能不处于有效状态的对象的功能。此外,在终结器线程上抛出异常会立即撕毁整个应用程序,而没有任何关于刚刚发生的事情的友好信息。

    这就是为什么Dispose模式实现始终具有对 GC.SuppressFinalize 这将导致终结器在对象已被释放并且GC可以在第一次运行时直接释放内存的情况下不运行。

    通常,如果您的应用程序能够在内存不足或线程中止以及随后的AppDomain卸载等严重异常中幸存下来,那么使用终结器可能非常复杂和棘手——这是SQL Server或IIS等应用程序的情况。

    长话短说:除非必须使用终结器,否则不要使用终结器;如果必须使用终结器(例如,使用非托管资源),则有相当多的研究等待着您。

    您可以在以下博客文章中找到有关此主题的更多文章:

    Eric Lippert - When everything you know is wrong

    Joe Duffy - Never write a finalizer again

        4
  •  0
  •   Vladimir P.    6 年前

    GC 电话 ~finalizer 在对象被收集之前。

    这意味着对象的托管成员也将被收集或已经被收集(我不知道GC如何工作的细微差别)。

    因此,已经没有必要清理托管成员,通常 Dispose(false) 在里面 ~定型器 防止它。

    ~B()
    {
        Dispose(false);
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }
    

    当我们通过调用手动处理对象时 Dispose() 方法或使用 using ,应清除对象的成员,并准备收集(将值设置为空等)。 所以,我们有 Dispose(true) 在里面 处置() 方法及 GC.SuppressFinalize(this); 禁用 ~定型器 调用,因为在对象成员已清理之后不必调用 Dispose(bool disposing) 两次。

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