Java Garbage Collect是否总是要“停止世界”?

我想更深入地了解Java的垃圾收集。

在HotSpot JVM世代集合中,在堆中,有三个区域(Young generation,Old generation和permanent generation)。 此外,还有两种算法:

1) Mark Sweep Compact

2) 并发标记和扫描

GC是否需要“停止世界”取决于它使用的算法而不是它运行的是哪一代,这是真的吗? 换句话说,如果我在所有三个区域使用1)作为GC算法,STW将永远发生?

此外,我理解不同的是第二个GC算法不需要压缩,最终会导致碎片。 那么第二个问题就是为什么压缩需要STW暂停?

压缩导致STW暂停的关键原因如下,JVM需要移动对象并更新对它的引用。 现在,如果您在更新引用之前移动对象,并且正在运行的应用程序从旧引用访问它而不是有问题。 如果您首先更新引用并且尝试移动对象,则更新的引用是错误的,直到移动对象并且对象未移动时的任何访问都将导致问题。

对于CMS和Parallel collecter,年轻代收集算法是相似的,它停止了世界,即当收集发生时应用程序被停止Stuff JVM正在做的是,标记所有可从根集到达的对象,将对象从Eden移动到幸存者空间和将已经超过终点阈值的集合中的对象移动到旧代。 当然JVM必须更新所有已移动对象的引用。

对于老一代并行收集器,在单个停止世界(STW)阶段中进行所有标记,压缩和参考更新,这导致GB中的堆的暂停(以秒为单位)。 对于具有严格响应时间要求的应用程序来说,这很痛苦。 到目前为止,Paralle collector仍然是吞吐量或批处理的最佳收集器(在Oracle Java中)。 事实上,我们已经看到了同样的情况,即使停顿时间比CMS更多并行收集器仍然可以获得更高的吞吐量,我认为这与更紧凑的空间局部性有关。

CMS通过同时进行标记解决了主要集合中的高暂停问题。 有2个STW部分,初始标记(从根集中获取引用)和备注暂停(标记结束时的小STW暂停,以便在标记和应用程序同时工作时处理对象图中的更改)。 对于几GB的堆大小和合理数量的应用程序线程,这两个暂停都在100 -200毫秒的范围内(记住更活跃的线程更多的根)

G1GC计划取代CMS并接受暂停目标。 通过逐步压缩堆来处理碎片。虽然工作是增量的,但你可以获得更小的暂停,但这可能会以更频繁的暂停为代价

在应用程序运行时,以上都不能压缩堆(CMS根本不压缩)。 AZUL GPGC垃圾收集甚至可以在不停止应用程序的情况下进行压缩,还可以处理参考更新。 因此,如果您想深入了解GC如何工作,那么值得阅读GPGC的算法。 AZUL将其作为无停顿的collections家推向市场。

openjdk中所有免费提供的GC都有一些停止世界事件。 而且不仅仅是GC,其他诸如去优化之类的东西也可以触发安全点。

但并非所有停顿都是平等的。 CMS和G1不需要使用旧代中的实时数据集来缩放其暂停时间,因为它们仅在暂停期间扫描对象的子集并且同时执行大部分工作,这与串行和吞吐量收集器不同。

正在进行一些工作以引入另一个收集器 ,该收集器将进一步将暂停时间与实时数据集大小分离。

另外存在其他GC实现,其避免全局暂停 – 它们可能仍然经历每线程暂停 – 或使得暂停持续时间O(1),即独立于实时数据集大小。 一个常用的例子是azul的C4收集器 。

那么第二个问题就是为什么压缩需要STW暂停?

压缩意味着移动物体。 移动对象意味着需要更新指针。 当应用程序线程仍在运行时,这是非常困难或昂贵的。

并发算法通常在吞吐量和复杂性方面花费一些成本来换取它们较短的暂停时间。 不进行压缩使得CMS相对(!)对于并发收集器来说简单。

这是一个链接,提供了有关java 8中不同收集器的一些很好的信息: https : //docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html#sthref27

所有战略都将停止世界。 但是,您的性能要求可以促使您选择不同的GC策略来提高性能或响应时间。

无论您选择哪种GC算法,都会出现Stop-the-world。 Stop-the-world意味着JVM正在阻止应用程序运行以执行GC。 当stop-the-world发生时,除了GC所需的线程之外的每个线程都将停止其任务。