我在回家的路上想着垃圾收集,我开始疑惑,为什么垃圾收集器完全冻结程序的执行?就个人而言,我本来会设计它来阻止任何尝试分配新对象的线程,但是运行的线程将保持不变。 我无法想象与垃圾收集器目前的工作方式相比,这会出现问题。
答案 0 :(得分:13)
我在回家的路上想着垃圾收集,我开始疑惑,为什么垃圾收集器完全冻结程序的执行?
GC设计中的延迟和吞吐量之间存在折衷。您可以单独处理堆分配的块(“增量”),也可以批量处理它们并同时处理它们(“停止世界”)。完全增量收集永远不会完全冻结程序,它具有非常低的延迟,但它的吞吐量也非常低。阻止世界垃圾收集器具有最差的延迟(一次冻结程序几秒甚至几分钟)但接近最佳吞吐量。
今天所有主要的生产GC都提供了一个中间层,通常采用分代收集,分批收集每个线程的苗圃世代,以及共享老一代的增量或并发收集。因此,只有托儿所收集会产生暂停,并且托儿所的大小是有限的,因此暂停时间保持较低,例如,使用工作站GC在.NET中10-100ms。
对于从不暂停的简单GC算法,请参阅Baker's Treadmill。有关垃圾收集的更多信息,我强烈推荐Memory Management Reference和Garbage Collection Handbook。
这里的其他答案中有很多错误的信息。 Jon Skeet编写了一些源代码,并从垃圾收集的角度开始讨论它。您需要非常小心这样做,因为源代码与GC看到的内容几乎没有对应关系。编译器执行指令块重新排列,寄存器分配,提升等,所有这些都会影响GC在运行时可见的内容。特别是,源代码中的范围不会传递给已编译的代码,并且通常会替换为liveness的相关概念。乔恩还写道,你必须停下来才能获得全球根源。虽然它是获得全局根源的最有效方法,并且由此产生的暂停几乎总是很小(亚毫秒),但这并非严格正确,因为您只需从每个线程复制不到一KB的堆栈。
Powerlord写道,移动的收藏家必须阻止读取,因此必须阻止读取的所有线程。这也不是真的。最简单的计数器示例是不可变数据:参照透明度意味着您可以安全地从任何副本中读取。
Kico写道,需要暂停才能确定可达性。这也不是真的。请参阅Dijkstra关于“即时”收藏家和任何最近实时GC的研究,例如Stacatto。
Jerry Coffin写下了最好的答案,但移动并不是GCs暂停的原因。有些GC不会移动但会暂停(例如HLVM)以及那些移动但不会停顿的GC(例如Stacatto)。
答案 1 :(得分:12)
现代垃圾收集器(无论如何,在.NET和Java中)实际上并没有“阻止世界” - 他们会同时收集各种聪明的东西。
但是,你可能想要考虑这样的情况:
object x = null;
object y = new object();
...
x = y;
y = null;
现在,假设GC查看x
,然后...
运行下方的行,然后GC查看y
- 它将看不到任何活动对象。 ..但对象仍然存在。
基本上需要有一定量的暂停才能获得一致的引用集。然后是压缩,参考重新分配等。然而,它并没有像过去那样需要在整个GC循环中停止所有事情。然而,确实会让人痛苦地思考:)
答案 2 :(得分:6)
除了Kico Lobo所说的,垃圾收集者还可以在内存中移动东西。
因此,他们不仅要阻止写入内存的线程,还要阻止从内存中读取的线程。
这是每个主题。
答案 3 :(得分:2)
大多数GC停止执行,因为对象可以在收集周期内在内存中移动(至少在最合理的最新设计中)。这意味着在错误的时间读取或写入几乎任何对象都可能导致问题。
有些收集器是围绕只是阻止读取(或写入)给定时间内被修改的内存的特定部分的想法而设计的,因此只要执行只使用不是(当前)的对象四处走动,它可以不受阻碍地进行。问题是大多数典型的硬件都没有为此提供有效的支持,因此即使它们原则上工作,它们在实践中也是相当低效的。至少有一次尝试使用这种类型的算法来使用典型寻呼单元中可用的写保护,但我不知道它已经被用于除了研究和实验以外的其他方面。
主要的替代方案是使收集器增量 - 即让它一次只执行少量工作,因此即使其他执行被停止,它也只需要停止在任何给定的时间都很少。
然而,随着多核机器变得如此普遍,我希望看到更多的工作投入到可以与其他执行并行运行的垃圾收集算法中。直到最近,主要重点是最大限度地减少垃圾收集所花费的总时间/精力。越来越多的可用核心可能(通常)意味着在垃圾收集中完成更多的工作可能很容易,如果这样做可以让代码的主流运行的障碍更少。
编辑:您可能想要阅读Paul Wilson的Survey of Uniprocessor Garbage Collection Techniques。这不是决定性的(尤其是考虑到它的年龄),但它至少是一个合理的起点。
答案 4 :(得分:1)
因为这是唯一可以确保其所有人都没有使用过它所引用的引用的方法。
如果它没有冻结执行,则无法保证。