C#中的`dispose pattern`:为什么我们需要`if(disposing)`条件?

问题描述 投票:1回答:2

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

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终结器时释放它们?

c# garbage-collection idisposable finalizer
2个回答
5
投票

Konrad Kokosa对他的书Pro .NET Memory Management有一个令人印象深刻的解释。 (重点补充)

在GC期间,在Mark阶段结束时,GC会检查终结队列以查看是否有任何可终结对象已死亡。如果它们是某些,它们还不能删除,因为它们的终结器需要执行。因此,此类对象被移动到另一个名为fReachable queue的队列。它的名字来自于它表示终结可到达对象的事实 - 现在只能由于最终化而可以到达的对象。如果找到任何此类对象,GC会向专用的终结器线程指示有工作要做。

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

此外,fReachable队列被视为在Mark阶段考虑的根,因为终结器线程可能不够快,无法在GC之间处理它的所有对象。这使得可终结的对象更多地暴露于中年危机 - 他们可能因为待定的最终化而停留在fReachable队列中一段时间​​而消耗第二代。

我认为这里的关键是:

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


0
投票

.NET中的对象存在,而对它们存在任何引用。一旦最后一个参考文献这样做,它们就不再存在。当对象存在时,对象使用的存储将永远不会被回收,但GC在回收存储之前会做一些事情:

  1. 有一个特殊的列表,称为“终结器队列”,它包含对已注册终结器的所有对象的引用。在识别出Universe中任何位置存在的每个其他引用之后,GC将检查终结器队列中的所有对象,以查看是否已找到任何引用。如果此进程导致它找到之前未发现的对象,则会将对另一个名为“freachable queue”的列表的引用复制。每当可释放队列非空并且没有终结器正在运行时,系统将从该队列中提取引用并在其上调用finalize。
  2. GC还将检查所有弱引用的目标,并使任何弱引用无效,其目标尚未被任何实时强引用识别。

请注意,finalize方法不会“垃圾收集”对象。相反,它会延长对象的存在,直到finalize被调用它为目的,以允许它满足它可能对外部实体的任何义务。如果此时在Universe中的任何位置都不存在对象的引用,则该对象将不再存在。

请注意,具有终结器的两个对象可以相互保持引用。在这种情况下,其终结者的运行顺序是未指定的。

© www.soinside.com 2019 - 2024. All rights reserved.