parNew+CMS,在minorGC发生前

作者: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

C++代码  
  1. // If the next generation is too full to accommodate worst-case promotion  
  2. // from this generation, pass on collection; let the next generation  
  3. // do it.  
  4. if (!collection_attempt_is_safe()) {  
  5.   gch->set_incremental_collection_failed();  // slight lie, in that we did not even attempt one  
  6.   return;  
  7. }  


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
C++代码  
  1. bool ConcurrentMarkSweepGeneration::promotion_attempt_is_safe(size_t max_promotion_in_bytes) const {  
  2.   size_t available = max_available();  
  3.   size_t av_promo  = (size_t)gc_stats()->avg_promoted()->padded_average();  
  4.   bool   res = (available >= av_promo) || (available >= max_promotion_in_bytes);  
  5.   return res;  
  6. }  

也就包含了楼主所问的检查:之前平均晋升的对象大小、当前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已经开始收集之后确实遇到了晋升失败的情况,最终的处理跟上面的流程基本一样。

你可能感兴趣的:(java)