猿问

C# 中的“处置模式”:为什么我们需要“if(处置)”条件?

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


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可防止终结器尝试释放可能已被回收的托管资源。

问题是:为什么假设被 object of 引用的对象SomeClass可能已经被释放,我们不应该在从 finalizer 调用方法时尝试释放它们?如果那些对象仍然被我们的SomeClass对象引用,它们就不能被释放,不是吗?据说:

那些有挂起(未运行)终结器的(暂时)保持活动状态,并被放入一个特殊的队列中。[...] 在每个对象的终结器运行之前,它仍然非常活跃——该队列充当根 对象

因此,我们的SomeClass对象再次被该队列引用(这与被根引用相同)。对象引用的其他对象SomeClass也应该是活动的(因为它们是通过SomeClass对象生根的)。那么为什么以及如何在SomeClass调用终结器时释放它们呢?


慕妹3146593
浏览 101回答 2
2回答

慕斯王

Konrad Kokosa 在他的《Pro .NET Memory Management 》一书中有一个令人印象深刻的解释。(强调)在 GC 期间,在标记阶段结束时,GC 检查终结队列以查看是否有任何可终结对象已死亡。如果它们是一些,它们还不能被删除,因为它们的终结器需要被执行。因此,这样的对象被移动到另一个名为 fReachable queue 的队列。它的名字来自于它代表最终化可达对象的事实——那些现在只因为最终化才可达的对象。如果找到任何此类对象,GC 会向专用终结器线程指示有工作要做。终结线程是由 .NET 运行时创建的另一个线程。它从 fReachable 队列中一个一个地移除对象并调用它们的终结器。这发生在 GC 恢复托管线程之后,因为终结器代码可能需要分配对象。由于此对象的唯一根已从 fReachable 队列中删除,下一次谴责此对象所在世代的 GC 将发现它不可访问并回收它。此外,fReachable 队列在 Mark 阶段被视为根,因为终结器线程可能不够快,无法在 GC 之间处理来自它的所有对象。这使可终结对象更多地暴露于中年危机——它们可能会停留在 fReachable 队列中一段时间,仅仅因为等待终结而消耗第 2 代。我认为这里的关键是:fReachable 队列在 Mark 阶段被视为根,因为终结器线程可能不够快,无法在 GC 之间处理来自它的所有对象。

慕的地10843

.NET 中的对象在存在对它们的任何引用时就存在。一旦最后一个引用不存在,它们就会不复存在。当对象存在时,对象使用的存储将永远不会被回收,但是 GC 在回收存储之前会做几件事:有一个特殊的列表,称为“终结器队列”,它包含对所有已注册终结器的对象的引用。在识别出 Universe 中任何地方存在的所有其他引用之后,GC 将检查终结器队列中的所有对象,以查看是否找到了对它们的任何引用。如果此过程导致它找到以前未发现的对象,它会将引用复制到另一个称为“freachable 队列”的列表。任何时候 freachable 队列非空并且没有终结器正在运行,系统将从该队列中提取一个引用并调用终结器。GC 还将检查所有弱引用的目标,并使目标未被任何有效强引用识别的任何弱引用无效。请注意,finalize 方法不会“垃圾收集”对象。相反,它会延长对象的存在直到finalize被调用,目的是允许它履行对外部实体可能具有的任何义务。如果那时在宇宙中任何地方都不存在对该对象的引用,则该对象将不复存在。请注意,两个具有终结器的对象可能会相互引用。在这种情况下,它们的终结器运行的顺序是未指定的。
随时随地看视频慕课网APP
我要回答