C#中的“处理方式”:为什么需要“如果(处理)”条件?

时间:2019-04-09 20:40:45

标签: c# garbage-collection idisposable finalizer

因此,默认的处置模式实现如下所示:

class SomeClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;

   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }

   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 

      if (disposing) {
         // Free any other managed objects here.
      }

      // Free any unmanaged objects here.
      disposed = true;
   }

   ~SomeClass()
   {
      Dispose(false);
   }
}

据说:

  

如果方法调用来自终结器(即,如果处置是   false),仅执行释放非托管资源的代码。因为   垃圾收集器销毁托管对象的顺序   未定义完成期间,使用以下命令调用此Dispose重载   值false阻止终结器尝试释放托管对象   可能已经被回收的资源。

问题是:为什么要假设SomeClass对象所引用的对象可能已被释放,并且在使用该方法时我们不应该尝试处置它们从终结器调用?如果这些对象仍被我们的SomeClass对象引用,则无法释放它们,不是吗?据说:

  

那些带有待定(未运行)终结器的组件将保持活动状态(暂时)并保持   放在一个特殊的队列中。 [...]在每个对象的定型器之前   运行,它仍然非常活跃-队列充当根   对象

因此,同样,我们的SomeClass对象被该队列引用(与根引用相同)。 SomeClass对象所引用的其他对象也应该都有效(因为它们是通过SomeClass对象植根的)。 然后为什么为什么以及如何在调用SomeClass终结器时释放它们?

2 个答案:

答案 0 :(得分:5)

Konrad Kokosa在他的书Pro .NET Memory Management中有一个令人印象深刻的解释。 (强调)

  

在GC期间,在Mark阶段结束时,GC检查终结队列,以查看是否有任何终结对象已死。如果有的话,它们还不能删除,因为将需要执行其终结器。因此,该对象被移动到另一个称为fReachable队列的队列。它的名字来自于这样一个事实,即它代表最终确定的可访问对象-现在仅由于最终确定而可访问的对象。如果发现任何此类对象,GC会向专用的终结器线程指示需要做的工作。

     

最终化线程是.NET运行时创建的另一个线程。 它从fReachable队列中一一删除对象,并调用其终结器。。 GC恢复托管线程后会发生这种情况,因为终结器代码可能需要分配对象。 由于从fReachable队列中删除了该对象的唯一根,因此下一个谴责该对象所处的世代的GC将发现它无法访问并回收它。

     

此外, fReachable队列被视为Mark阶段考虑的根,因为终结器线程可能不够快,无法在GC之间处理来自该终结器线程的所有对象。这使这些可终结化的对象更容易遭受中年危机的困扰-由于即将完成的终结工作,它们可能在fReachable队列中消耗了第二代资源。

我认为这里的关键是:

  

fReachable队列被视为Mark阶段考虑的根,因为终结器线程可能不够快,无法在GC之间处理来自该队列的所有对象。

答案 1 :(得分:0)

.NET中的对象存在,而对其存在任何引用。最后一个参考文献一经存在,它们便不复存在。对象存在时,将永远不会回收该对象使用的存储,但是在回收存储之前,GC需要执行以下几件事:

  1. 有一个特殊的列表,称为“ finalizer队列”,其中包含对已注册终结器的所有对象的引用。在确定了Universe中任何其他地方存在的所有其他引用之后,GC将检查终结器队列中的所有对象,以查看是否找到了对它们的任何引用。如果此过程使它找到以前未发现的对象,则将引用复制到另一个列表,称为“可到达队列”。每当可访问性队列为非空且没有终结器运行时,系统将从该队列中提取引用并对其调用终结器。

  2. GC还将检查所有弱引用的目标,并使那些未被任何实时强引用标识的目标的弱引用无效。

请注意,finalize方法不会“垃圾收集”对象。取而代之的是,它延长了对象的存在,直到调用finalize为止,目的是允许其履行对外部实体可能承担的任何义务。如果那时在宇宙中的任何地方都没有对该对象的引用,那么该对象将不复存在。

请注意,两个带有终结器的对象可能会相互保持引用。在这种情况下,终结器的运行顺序不确定。