为什么GC将对象放在终结队列中?

时间:2011-04-11 23:42:56

标签: c# .net garbage-collection

据我所知,一旦我实现了类的析构函数,c#中的垃圾收集器就会将类的所有对象放入终结队列中。当我阅读GC.Suppresfinalize的文档时,它提到对象头已经设置了一个用于调用finalize的位。

我想知道为什么GC的实现者必须将所有对象放入队列中,并将内存释放延迟1-2个周期。他们不能只是在释放内存时查看位标志,然后调用对象的finalize然后释放内存?

毫无疑问,我是一个白痴,我无法理解GC的工作。我提出这个问题只是为了提高我的理解或填补我知识中缺失的空白

编辑:如果位标志用于suppressfinalize,GC实现者可能为此目的在对象头中添加了另一个标志,不是吗?

5 个答案:

答案 0 :(得分:12)

因此它可以在不同的线程中运行,从而防止阻塞主GC线程。

您可以从MSDN article了解GC的很多内容。

答案 1 :(得分:4)

这里有一个很好的解释

What are the Finalizer Queue and Control+ThreadMethodEntry?

基本上,推理可能并不总是理想的GC必须等待终结器代码执行,因此排队终结器允许推迟推迟到更方便的时候。

答案 2 :(得分:1)

垃圾收集暂停最好尽可能短。为此,运行终结器通常会延迟到稍后的时间,此时垃圾收集的疯狂工作已经完成。而是在后台完成一个单独的线程。

答案 3 :(得分:1)

@Jason:对于 f-reachable queue ,情况属实。但恕我直言,它没有解释为什么有终结队列本身。

我的猜测是,终结队列可以添加其他信息,帮助GC区分对象生命的所有可能状态 - 周期

对象标题中的终结标志表示“对象需要最终确定”或“对象不需要最终确定”,但它没有说明是否已经完成了最终化。

但说实话,我不明白为什么在当前的最终确定流程实施中需要它。

事实上,这是我想象的可能没有终结队列的天真工作流程:

  • 创建对象时,如果它有终结器 GC 会设置终结标志;
  • 如果稍后调用 SupressFinalize ,则该标志为零;
  • 现在让我们跳转到GC收集对象时,它不会从任何地方引用:如果设置了终结标志,那么GC会将对象的引用放入 f-reachable queue 并且让终结线程运行;
  • 稍后终结线程将引用出列,重置终结标志并运行终结器;
  • 如果对象想要 refinalized ,以后可以 ReRegisterForFinalize 再次设置终结标志;
  • 以后GC再次收集对象:如果没有设置终结标志,它知道没有任何事情要做,然后释放对象内存;
  • 如果设置了终结标志,则GC再次将对象的引用排入f-reachable队列,然后我们再次进行另一轮;
  • 在某个时间点,对象很开心,完成最终确定并收集;或者app-domain或进程关闭,无论如何都要释放内存。

所以在这些场景中似乎不需要终结队列,只有终结标志才有用。

一个可能的原因是,从概念的角度来看,可能存在如下规则:“当且仅当未从任何根引用时才收集对象”。 因此,没有最终化队列,并且基于对象状态本身收集对象的决定,检查终结标志,与此规则不兼容。

但实际上我并不认为GC的实施是基于这种理论规则的教条式应用,而只是基于实用的选择;所以很明显我错过了一些关键场景,GC需要终结队列来知道在收集对象时该做什么,但是哪些

答案 4 :(得分:1)

垃圾收集器不识别和检查垃圾,除非处理大对象堆。相反,它的行为就像一个保龄球场置瓶机去除投掷之间的枯木:置瓶机抓住所有仍然站立的别针,将它们从车道表面抬起,然后在不考虑的情况下穿过车道的清扫杆表面上有多少个针脚。扫除内存批发比识别要删除的单个对象要快得多。如果1%的对象具有终结器(实数可能更少),那么有必要检查100个对象头以找到每个可终结对象。拥有一个单独的具有终结器的对象列表使得GC不必查看任何没有终结器的垃圾对象。