Language agnostic 为什么垃圾收集器会冻结执行?

Language agnostic 为什么垃圾收集器会冻结执行?,language-agnostic,garbage-collection,Language Agnostic,Garbage Collection,我在回家的路上想到了垃圾收集,我开始想,为什么垃圾收集器会完全冻结程序的执行?就我个人而言,我会设计它来阻止任何试图分配新对象的线程,但运行的线程将被单独留下。 与垃圾收集器当前的工作方式相比,我无法想象在任何情况下这都会是一个问题。,因为这是它确保要清理的引用不被任何其他人使用的唯一方法 如果它没有冻结执行,就无法保证这一点。除了Kico Lobo所说的,垃圾收集器还可以在内存中移动东西 因此,它们不仅要阻止写入内存的线程,还要阻止从内存读取的线程 这是每一个线程。现代垃圾收集器(无论如何,在

我在回家的路上想到了垃圾收集,我开始想,为什么垃圾收集器会完全冻结程序的执行?就我个人而言,我会设计它来阻止任何试图分配新对象的线程,但运行的线程将被单独留下。
与垃圾收集器当前的工作方式相比,我无法想象在任何情况下这都会是一个问题。

,因为这是它确保要清理的引用不被任何其他人使用的唯一方法


如果它没有冻结执行,就无法保证这一点。

除了Kico Lobo所说的,垃圾收集器还可以在内存中移动东西

因此,它们不仅要阻止写入内存的线程,还要阻止从内存读取的线程

这是每一个线程。

现代垃圾收集器(无论如何,在.NET和Java中)实际上并没有“阻止世界”——它们会同时执行各种聪明的操作来收集垃圾

<>但是,你可能想考虑这样的情况:

 object x = null;
 object y = new object();
 ...
 x = y;
 y = null;
现在,假设GC查看
x
,然后运行
..
下面的行,然后GC查看
y
-它不会看到任何活动对象。。。但是这个物体应该仍然是活的


基本上,为了获得一组一致的引用,需要一定量的暂停。然后是压缩、引用重新分配等。然而,就要求在整个GC循环中停止所有操作而言,这已经不像过去那么糟糕了。然而,思考起来确实会很痛苦:)

大多数GCs会停止执行,因为对象可以在收集周期中在内存中移动(至少对于最新的设计是如此)。这意味着在错误的时间阅读或书写几乎任何对象都可能导致问题

有些收集器的设计理念是只阻止在给定时间修改的内存特定部分的读取(或写入),因此只要执行只使用(当前)未移动的对象,它就可以不受阻碍地进行。问题是,大多数典型的硬件都不能为这一点提供有效的支持,因此即使它们在原则上工作,在实践中也相当低效。至少有一次尝试调整这种类型的算法,以使用典型分页单元中可用的写保护,但我不知道它除了用于研究和实验之外,还用于其他许多方面

主要的替代方法是使收集器增量化,即让它一次只做少量的工作,因此即使其他执行停止,它在任何给定的时间也只需要停止一小部分

然而,随着多核机器变得如此普遍,我希望看到更多的工作投入到垃圾收集算法中,这些算法可以与其他执行并行运行。直到最近,主要的重点还是最小化在垃圾收集上花费的总时间/精力。越来越多的可用内核可能(通常)意味着在垃圾收集中做更多的总工作可能很容易被证明是合理的,如果这样做可以让主流代码在更少的障碍下运行的话

编辑:你可能想读保罗·威尔逊的。这并不是决定性的(特别是考虑到它的年龄,现在更是如此),但它至少是一个合理的起点

我在回家的路上想到了垃圾收集,我开始想,为什么垃圾收集器会完全冻结程序的执行

在GC设计中,延迟和吞吐量之间需要权衡。您可以单独处理堆分配的块(“增量”),也可以对它们进行批处理并同时处理所有块(“停止世界”)。完全增量收集不会完全冻结程序,延迟非常低,但吞吐量也非常低。Stop the world垃圾收集器的延迟可能最差(一次将程序冻结几秒钟甚至几分钟),但接近最佳吞吐量

如今,所有主要的生产GCs都提供了一个中间地带,通常是分代收集,每线程苗圃世代分批收集,以及共享旧世代的增量或并发收集。因此,只有托儿所集合会发生暂停,并且托儿所大小是有限制的,因此暂停时间保持较低,例如,在工作站GC的.NET中为10-100ms

有关从不暂停的简单GC算法,请参阅。有关垃圾收集的更多信息,我强烈推荐和

其他答案中有很多错误信息。Jon Skeet编写了一些源代码,并开始从垃圾收集的角度对其进行讨论。这样做需要非常小心,因为在源代码和GC看到的内容之间几乎没有对应关系。编译器执行指令块重排、寄存器分配、升级等操作,所有这些都会影响GC在运行时可见的内容。特别是,源代码中的作用域并没有传递到编译后的代码中,通常被相关的。乔恩还写道,你必须暂停,以获得全球根。虽然这是获取全局根的最有效的方法,但严格来说,这并不是真的,并且产生的暂停几乎总是很小(亚毫秒),因为您只是从每个线程复制不到1KB的堆栈

Powerlord写道,移动收集器必须阻止读取,因此,必须阻止所有读取的线程。这也不是事实。最简单的反例是不可变数据:引用透明意味着您可以安全地读取任何副本

Kico写道,需要暂停来确定可达性。这也不是事实。请参阅Dijkstra关于“动态”收集器和任何最近的实时GC的研究,如

杰里·科芬写了最好的答案,但莫文没有