作者:RednaxelaFX
链接:http://hllvm.group.iteye.com/group/topic/42365
ParNew/CMS组合在一起用的时候,
ParNewGeneration::collect():
http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/312b5f1dc31d/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp#l948
- // If the next generation is too full to accommodate worst-case promotion
- // from this generation, pass on collection; let the next generation
- // do it.
- if (!collection_attempt_is_safe()) {
- gch->set_incremental_collection_failed(); // slight lie, in that we did not even attempt one
- return;
- }
collection_attempt_is_safe()的定义在基类DefNewGeneration里:
http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/312b5f1dc31d/src/share/vm/memory/defNewGeneration.cpp#l865
这里它基本上就是交给CMS的ConcurrentMarkSweepGeneration::promotion_attempt_is_safe(size_t max_promotion_in_bytes)去计算最大可能晋升的对象大小跟CMS generation剩余空间的关系:
http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/312b5f1dc31d/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp#l898
- bool ConcurrentMarkSweepGeneration::promotion_attempt_is_safe(size_t max_promotion_in_bytes) const {
- size_t available = max_available();
- size_t av_promo = (size_t)gc_stats()->avg_promoted()->padded_average();
- bool res = (available >= av_promo) || (available >= max_promotion_in_bytes);
- return res;
- }
也就包含了楼主所问的检查:之前平均晋升的对象大小、当前young generation已使用的大小(也就是最大可能晋升的对象大小)跟当前CMS的最大可用空间大小相比较。只要CMS的剩余空间比前两者的任意一者大,CMS就认为晋升还是安全的;反之亦然。
楼主所问的“之前每次晋升到老年代的平均大小大于年老代的剩余空间大小”如果外加“当前minor GC时ParNew里已使用空间同样大于CMS年老代的剩余空间大小”,那么ParNew的minor GC就会在通知CMS说“我不干了”之后直接跳过自己。
这样就会回到GenCollectedHeap::do_collection()的循环里。GCH发现ParNew不干了之后会问CMS年老代要不要收集,CMS基本上只能回答:要。
然后CMS会根据 UseCMSCompactAtFullCollection 、 CMSFullGCsBeforeCompaction 和当前收集状态去决定是: (1) 使用跟Serial Old GC一样的LISP2算法的mark-compact来做full GC ,还是 (2) 用CMS自己的mark-sweep来做不并发的(串行的)old generation GC 。后者在CMS里也叫做foreground collector(正常的并发模式的CMS是background collector)。
http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/312b5f1dc31d/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp#l1933
如果发生晋升失败的时候CMS已经正在执行一个并发收集,那么那个并发收集就会被打断,各种状态复位之后转为执行上面选择的两种串行收集之一。
UseCMSCompactAtFullCollection默认为true,CMSFullGCsBeforeCompaction默认是0,这样的组合保证CMS默认不使用foreground collector,而是用Serial Old GC的方式来在promotion failed或者可能会fail的时候进行full GC。
JDK9的一个新变更就要去掉这两个参数及其背后的代码,彻底去掉CMS的foreground GC功能: JEP 214: Remove GC Combinations Deprecated in JDK 8 。为了与未来接轨大家也请不要乱配置这俩参数了喔。
要注意上述两种可能性不但算法不一样,收集的范围也不一样。
Serial Old GC的算法是mark-compact(也可以叫做mark-sweep-compact,但要注意它不是“mark-sweep”)。具体算法名是LISP2。它收集的范围是整个GC堆,包括Java heap的young generation和old generation,以及non-Java heap的permanent generation。因而它是full GC。
CMS的foreground collector的算法就是普通的mark-sweep。它收集的范围只是CMS的old generation,而不包括其它generation。因而它在HotSpot VM里不叫做full GC。
========================================
顺带一提,如果在ParNew准备收集时CMS说晋升没问题,但ParNew已经开始收集之后确实遇到了晋升失败的情况,最终的处理跟上面的流程基本一样。