2 回答
TA贡献1864条经验 获得超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 之间处理来自它的所有对象。
TA贡献1785条经验 获得超8个赞
.NET 中的对象在存在对它们的任何引用时就存在。一旦最后一个引用不存在,它们就会不复存在。当对象存在时,对象使用的存储将永远不会被回收,但是 GC 在回收存储之前会做几件事:
有一个特殊的列表,称为“终结器队列”,它包含对所有已注册终结器的对象的引用。在识别出 Universe 中任何地方存在的所有其他引用之后,GC 将检查终结器队列中的所有对象,以查看是否找到了对它们的任何引用。如果此过程导致它找到以前未发现的对象,它会将引用复制到另一个称为“freachable 队列”的列表。任何时候 freachable 队列非空并且没有终结器正在运行,系统将从该队列中提取一个引用并调用终结器。
GC 还将检查所有弱引用的目标,并使目标未被任何有效强引用识别的任何弱引用无效。
请注意,finalize 方法不会“垃圾收集”对象。相反,它会延长对象的存在直到finalize
被调用,目的是允许它履行对外部实体可能具有的任何义务。如果那时在宇宙中任何地方都不存在对该对象的引用,则该对象将不复存在。
请注意,两个具有终结器的对象可能会相互引用。在这种情况下,它们的终结器运行的顺序是未指定的。
- 2 回答
- 0 关注
- 100 浏览
添加回答
举报