ParallelGC和ParallelOldGC有什么区别?

31

我对GC算法有一些问题:

首先,当我们使用参数例如UseSerialGCUseParallelGCUseParallelOldGC等时,我们指定了一个GC算法。它们每个算法都可以在所有代上进行GC,是吗?

例如,如果我使用java -XX:+UseSerialGC,所有代将使用串行GC作为GC算法。

其次,我是否可以在老年代中使用ParallelGC,而在年轻代中使用SerialGC

最后,作为标题,ParallelGCParallelOldGC之间有什么区别?

4个回答

24

请查看HotSpot VM选项

-XX:+UseParallelGC = 使用并行垃圾收集器进行Scavenges。 (在1.4.1中引入)。

-XX:+UseParallelOldGC = 在完整收集时使用并行垃圾回收。 启用此选项会自动设置-XX:+UseParallelGC。(在5.0更新6中引入)

其中,Scavenges指的是年轻代GC。


那个页面上哪里回答了问题1和2?我看不到! - Stephen C
谢谢。ParNew GC和Parallel GC有什么区别?我知道在StackOverflow上已经有了答案,但我没看懂。你能帮我吗? - leo
2
第一张表格展示了年轻代和老年代的不同GC收集器算法。http://blog.ragozin.info/2011/07/hotspot-jvm-garbage-collection-options.html - Nathan
“-XX:+UseParallelOldGC” 在我之前编译的 JDK 15 版本中被报告为已删除/忽略。不太确定原因,但如果有人知道的话,我很想知道 :) - jocull
好的,谢谢您的提醒。ParallelOldGC已被弃用,因为它很少被使用且需要大量维护工作。详情请参见https://openjdk.java.net/jeps/366。 - michaelok

13

经过大量的搜索和研究,我了解到以下内容:

-XX:+UseParallelGC - 这使得 GC 在年轻代使用多个线程,但对于老年代仍使用串行标记和压缩算法。

-XX:+UseParallelOldGC - 这使得 GC 在老年代使用并行标记和压缩算法。

让我们来理解一下 -

年轻代中工作的算法和内存布局(例如标记和复制、交换空间等),在老年代中由于很多原因而无法工作。

低死亡率 - 在老年代中,“死亡率”显著低于年轻代。在典型的 Java 应用程序中,大多数对象很快就消失了,只有少数生存更长时间。随着在年轻代幸存并提升到老年代的对象们,这些对象倾向于生存更长的时间。这导致老年代的死亡率比年轻代非常低。

大小显著 - 旧一代比年轻一代大得多。因为年轻一代很快被清理,相对较少的空间可用于许多短寿命对象(小年轻一代)。在老一代中,随着时间的推移,对象会积累。因此,老一代中必须有比年轻一代更多的空间(大老一代)。

分配较少 - 在老一代中,分配比年轻一代少得多。这是因为在老一代中,只有当垃圾收集器将幸存的对象从年轻一代晋升到老一代时才会产生对象。另一方面,在年轻一代中,应用程序生成的所有对象(即大多数分配)都发生在年轻一代中。

考虑到这些差异,已选择了一个 Young Generation 的算法,以尽快完成垃圾收集,因为由于高死亡率 [点(1)],它必须经常被调用。此外,该算法必须确保尽可能高效的内存分配 [点(3)],因为大部分分配发生在年轻一代中。Young Generation 上的标记-复制算法具有这些属性。

另一方面,这个算法在旧代上没有意义。情况是不同的:垃圾收集器必须处理旧代中许多对象 [点(2)],其中大部分仍然存活;只有一小部分变得不可达并可以释放 [点(1)]。如果垃圾收集器每次进行垃圾收集时都像标记-复制一样复制所有幸存对象,那么它将花费很长时间进行复制而几乎不会获得任何好处。

因此标记-清除算法用于旧代,其中不复制任何内容,仅释放不可达对象。由于此算法导致堆的碎片化,因此人们考虑了标记-清除算法的变体,在扫描阶段后进行压缩,从而减少了碎片化。这种算法被称为标记-整理算法。

标记-整理算法可能需要消耗大量时间,因为它需要按照以下四个阶段遍历对象图:

  1. 标记
  2. 计算新位置
  3. 引用调整
  4. 移动
计算新位置阶段中,每当它获得一个空闲空间时,尝试找到一个可以移动到该空间的对象(碎片整理)。将该对存储以供后续阶段使用。这会导致算法花费更多时间。
虽然标记和比较解决了一些特定于老年代的问题,但它也存在严重问题,因为这是一个STW(停止整个世界)事件,并且需要很长时间,可能会严重影响应用程序。 老年代的替代算法 为了减少中断时间,已经考虑了一些串行标记-压缩算法的替代方法: 并行标记-压缩算法仍然锁定所有应用程序线程,但然后使用多个垃圾收集器线程处理标记和随后的压缩。虽然这仍然是一种整个世界停止的方法,但在多核或多处理器机器上产生的暂停时间比串行标记-压缩算法短。这个老年代的并行算法(称为“ParallelOld”)自Java 5 Update 6以来就已经可用,并使用选项-XX: + UseParallelOldGC进行选择。

一种竞争的标记-清除算法,至少部分地与应用程序竞争,偶尔需要短暂的“停止-全球”阶段。这个并发的标记和清除算法(称为“CMS”)自Java 1.4.1以来就存在了;它通过选项-XX:+UseConcMarkSweepGC打开。重要的是,这只是一个标记和清除算法;不进行压缩,导致已经讨论过的碎片问题。

因此,在简洁起见,-XX: + UseParallelOldGC被用作使用标记和压缩算法进行主要收集时使用多个线程的指示。如果使用这个选项,小的或年轻的收集是并行的,但是大的收集仍然是单线程的。

希望这回答了您的问题。


嗯,我有点困惑。如果我设置了-XX:+ UseParallelGC或XX:+ UseParallelOldGC,为什么通过JMX MBeans在java.lang中看到相同的值: -java.lang:type = GarbageCollector,name = PS MarkSweep -java.lang:type = GarbageCollector,name = PS Scavenge 这是否意味着没有区别? 在OpenJDK 64位服务器VM(11.0.1 + 13,混合模式)上进行了测试。 - elkoo
明白了,这是因为这个变更:https://bugs.openjdk.java.net/browse/JDK-6679764 和 http://hg.openjdk.java.net/lambda/lambda/hotspot/file/24cae3e4cbaa/src/share/vm/runtime/arguments.cpp#l1430 但是,GC策略本身是不同的。 - elkoo

2

这些是应用于Java Heap的不同区域,即New和Old generations的两种gc策略。以下链接有助于澄清哪些选项意味着其他选项。特别是在开始理解指定ParallelOldGC或ParNewGC时,它非常有帮助。 http://www.fasterj.com/articles/oraclecollectors1.shtml


1

来自Oracle Java SE 8文档:

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html

并行收集器(也称为吞吐量收集器)可以并行执行小型收集,从而显著减少垃圾回收开销。它适用于在多处理器或多线程硬件上运行的中等到大型数据集的应用程序。在某些硬件和操作系统配置上,默认选择并行收集器,也可以使用选项-XX:+UseParallelGC显式启用。

并行压缩是一项功能,使并行收集器能够并行执行主要收集。如果没有并行压缩,则使用单个线程执行主要收集,这可能会严重限制可扩展性。如果指定了选项-XX:+UseParallelGC,则默认情况下也将使用多个线程进行主要收集。关闭它的选项是-XX:-UseParallelOldGC

因此,如果您指定-XX:+UseParallelGC,则默认情况下也将使用多个线程进行主要收集。反之亦然,即如果您指定-XX:+UseParallelOldGC,则小型收集也将使用多个线程进行。


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