Java 7中G1垃圾收集器在并发标记阶段中为什么会中止?

14

我注意到在使用G1垃圾收集器的应用程序中偶尔会发生完整GC,我正在尝试弄清楚为什么会发生这种情况。

从一个区域扫描开始到下一个区域扫描开始的循环如下所示。 在61807.406时,记录了一次完整GC,接着是关于并发标记终止的条目。 我想知道为什么GC感觉需要进行完整的、停止一切的垃圾回收,以及如何避免它发生。

请注意,此问题似乎曾在OpenJDK邮件列表上提出过,但没有得到回复。

为简洁起见,我已经删除了Young GC的详细信息,但如果需要,我可以在其他地方发布完整的内容块。

61805.878: [GC concurrent-root-region-scan-start]
61805.882: [GC concurrent-root-region-scan-end, 0.0033586]
61805.882: [GC concurrent-mark-start]
61806.133: [GC pause (young), 0.02836202 secs]
   [Eden: 498M(498M)->0B(478M) Survivors: 14M->34M Heap: 3025M(4096M)->2548M(4096M)]
 [Times: user=0.19 sys=0.00, real=0.03 secs] 
61806.426: [GC pause (young), 0.02766222 secs]
   [Eden: 478M(478M)->0B(480M) Survivors: 34M->32M Heap: 3050M(4096M)->2576M(4096M)]
 [Times: user=0.19 sys=0.00, real=0.03 secs] 
61806.717: [GC pause (young), 0.02214895 secs]
   [Eden: 480M(480M)->0B(502M) Survivors: 32M->10M Heap: 3056M(4096M)->2571M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61807.000: [GC pause (young), 0.01899188 secs]
   [Eden: 502M(502M)->0B(502M) Survivors: 10M->10M Heap: 3074M(4096M)->2573M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61807.201: [GC pause (young), 0.02619259 secs]
   [Eden: 162M(502M)->0B(500M) Survivors: 10M->12M Heap: 3036M(4096M)->2876M(4096M)]
 [Times: user=0.11 sys=0.00, real=0.03 secs] 
61807.283: [GC pause (young), 0.02068515 secs]
   [Eden: 102M(500M)->0B(500M) Survivors: 12M->12M Heap: 3058M(4096M)->2957M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61807.350: [GC pause (young), 0.01606520 secs]
   [Eden: 52M(500M)->0B(498M) Survivors: 12M->14M Heap: 3020M(4096M)->2969M(4096M)]
 [Times: user=0.11 sys=0.00, real=0.02 secs] 
61807.389: [GC pause (young), 0.01573865 secs]
   [Eden: 42M(498M)->0B(500M) Survivors: 14M->12M Heap: 3021M(4096M)->2978M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61807.406: [Full GC 2978M->2498M(4096M), 4.8896638 secs]
 [Times: user=6.37 sys=0.08, real=4.89 secs] 
61812.296: [GC concurrent-mark-abort]
61812.542: [GC pause (young), 0.01526403 secs]
   [Eden: 512M(500M)->0B(510M) Survivors: 0B->2048K Heap: 3018M(4096M)->2506M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61812.793: [GC pause (young) (initial-mark), 0.01391544 secs]
   [Eden: 510M(510M)->0B(508M) Survivors: 2048K->4096K Heap: 3016M(4096M)->2508M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.01 secs] 
61812.807: [GC concurrent-root-region-scan-start]

这是使用Java Hotspot 1.7.0_7版本,具有以下有趣设置:

-XX:PermSize=128m
-XX:MaxPermSize=128m
-XX:NewSize=512m
-XX:MaxNewSize=512m
-Xms4096m
-Xmx4096m
-XX:+UnlockDiagnosticVMOptions
-XX:+UnsyncloadClass
-XX:+UseTLAB
-XX:+UseG1GC
-XX:SurvivorRatio=10
-Xloggc:./workspace/gc.log
-verbose:gc
-XX:+PrintGC
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
1个回答

8

我想你已经了解这个参考文献这个页面也很有用。

当老年代对象(在短暂的(年轻)代中存活的对象)填满为它们分配的空间时,将发生Full GC。当发生Full GC时,必须中止任何正在进行的短暂标记。

减少老年代填充速度需要添加更多堆/内存或调整可用内存在老年代和年轻代之间的分配。参数NewSizeMaxNewSizeNewRatio是用于后者的。实验是找到有效方法的唯一途径。

通常认为,将比例调整为使老年代变大会减少完整收集的数量。在许多情况下,这是正确的,但并不总是如此。存在一种情况,许多老年代对象在tenured后不久就死亡。换句话说,它们应该在年轻代中被收集,但它们的生命结束得有点晚。在这种情况下,扩大年轻代可以使这些对象在那里被收集,而不是tenured。其症状是完整收集导致分配空间大幅减少。

这似乎不是您的情况:2978M->2498M。您唯一的出路可能是增加堆大小,根据需要购买更多内存。但几乎所有长时间运行的系统都会偶尔进行完整收集。


几个问题:'Full GC' 发生在 tenured 对象... 填满了为它们分配的空间。'我的 tenured 空间应该是 3.5g(4g heap - 512m MaxNewSize)。从 full gc 前的 young collections 中看,tenured 中的数据量似乎是 3g。3.5 > 3,所以我认为不需要 full gc 也不需要进行 GC。我是否理解错误? - sharakan
尽管我认为这主要是由于碎片化,但运行时间长的几乎所有系统都会偶尔进行完整收集。我知道在Java6的CMS中是这样的。我原以为在G1中只有在标记线程无法跟上垃圾产生的情况下才会出现这种情况,但通过适当的调整应该是可以避免的。你认为在G1中必须偶尔运行完整的gc的任何原因吗? - sharakan
G1使用许多区域。当垃圾回收失败时,会发生完整的收集:没有空闲区域可用于压缩。系统运行时间越长,每个区域至少有一个活动对象的机会就越大。 - Gene
1
很遗憾没有权威的资料。有一个可调参数的免费推广空间。当区域>50%满时,总量超过允许值,GC无法做任何其他事情!引用区域之间的碎片化的人包括这些:http://web.archiveorange.com/archive/v/Dp7RfmtOWB5JGyYecb8E和http://comments.gmane.org/gmane.comp.db.cassandra.user/28034。找不到其他我见过的人。另请参阅http://openjdk.java.net/jeps/156关于类卸载和http://www.quora.com/What-is-the-status-of-the-implementation-of-the-G1-garbage-collector-for-the-JVM,以解决类似于您的问题。 - Gene
FYI,你提供的第一、第二和第四个链接的主要作者是同两个人。 (第二个和第四个链接是他们各自独立发布的帖子,而第一个链接是他们之间的对话。)这并不是贬低他们经验的有效性,只是说你的样本量比表面上看起来要小些。 - Tim
你们应该意识到,当你指定年轻代GC选项(如NewRatio)时,会禁用G1中的自适应启发式算法,对吧?使用G1 GC时,应该从默认设置开始,除非你有非常充分的理由去更改它们。请参阅http://www.infoq.com/presentations/java-g1?utm_source=infoq&utm_medium=related_content_link&utm_campaign=relatedContent_articles_clk#downloadPdf。 - steinybot

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