JVM为什么需要在进行Full GC时停止运行?

75

我认为是因为JVM需要移动对象,这正确吗?


3
请查看维基百科关于垃圾回收的页面,链接如下:http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29#Stop-the-world_vs._incremental_vs._concurrent - Paul Whelan
4个回答

91

首先,维基百科的垃圾回收文章非常好阅读。

一般来说,GC不需要Stop-the-World暂停。有些JVM实现几乎是无暂停的(例如Azul Zing JVM)。JVM何时需要STW来收集垃圾取决于它所使用的算法。

Mark Sweep Compact(MSC)是默认在HotSpot中使用的流行算法。它以STW方式实现,具有3个阶段:

  • MARK - 遍历存活对象图以标记可达对象
  • SWEEP - 扫描内存以查找未标记内存
  • COMPACT - 重定位标记对象以整理自由内存

在堆中重定位对象时,JVM应该更正对该对象的所有引用。在重定位过程中,对象图不一致,这就是为什么需要STW暂停的原因。

Concurrent Mark Sweep(CMS)是另一个HotSpot JVM中的算法,它不为老年代收集(不完全与全集合相同)使用STW暂停。

CMS利用写屏障(每次在Java堆中写入引用时触发的触发器)来实现MARK的并发版本,不使用COMPACT。缺乏压缩可能会导致碎片化,如果后台垃圾收集不够快,应用程序仍然可能被阻塞。在这些情况下,CMS将回退到STW标记-扫描-压缩收集。

还有G1,它是MSC的增量变体。您可以在我博客中阅读更多有关HotSpot JVM中的GC算法


1
自从RedHat推出了另一个GC算法,现在已经有了另一种GC算法。请参考http://openjdk.java.net/projects/shenandoah/。 - Keynan
2
根据这个Oracle页面,https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html#concurrent_mark_sweep_cms_collector,CMS收集器在并发收集周期中会暂停应用程序两次,包括初始标记暂停和重新标记暂停。如果存在过多的碎片化和不足够的块,则会导致第三次暂停。 - Javdroider

10

使用吞吐量GC,JVM需要STW暂停来释放尽可能多的内存。只有使用这样的暂停,它才是最有效的。

使用低暂停收集器(CMS),您可以同时清理老年代空间,而无需暂停应用程序。缺点是老年代变得分散。如果它过于分散并且需要压缩,则会发生Full GC(STW)。但是,您始终可以调整应用程序以使其不产生任何Full GC。

G1 GC是一个特例。它当前的主要目标是在堆上具有低碎片化,同时仍然是并发的(就像CMS一样)。当它无法达到此目标时,JVM也会回到STW暂停,以便堆完全清理和压缩。


1
需要在几乎任何垃圾回收方案中扫描堆栈上的引用,即使在大多数最小化暂停的方案中也是如此。有一个很好的详细解释this answer。 增量和并发算法努力将这些停顿最小化,但在大多数情况下仍然会有它们。实际上,甚至有一些移动/压缩方法不需要停止对象移动(例如Staccato)。

1
停止-全球(stop-the-world)保证在收集器运行时不会分配新对象,也不会突然出现对象不可达的情况。
优点是它比增量垃圾收集更简单实现且速度更快。

6
GC标记为可达后,对象变得不可访问并不是一个大问题。它是正确的,并且只会延迟一次GC周期内的内存回收。更严重的问题是在GC期间分配新对象,而唯一的引用存储在已经访问过的对象中 - 那么该对象将被错误地回收。然而,这不是一个无法解决的问题。事实上,自70年代以来就已经解决了。 - user395760

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接