| 
                        副标题[/!--empirenews.page--]
                         经常有同学会问,为啥我的应用 Old Gen 的使用占比没达到 CMSInitiatingOccupancyFraction 参数配置的阈值,就触发了  CMS GC,表示很莫名奇妙,不知道问题出在哪? 
  
其实 CMS GC 的触发条件非常多,不只是 CMSInitiatingOccupancyFraction 阈值触发这么简单。本文通过源码全面梳理了触发  CMS GC 的条件,尽可能的帮你了解平时遇到的奇奇怪怪的 CMS GC 问题。 
先抛出一些问题,来吸引你的注意力。 
    - 为什么 Old Gen 使用占比仅 50% 就进行了一次 CMS GC?
 
    - Metaspace 的使用也会触发 CMS GC 吗?
 
    - 为什么 Old Gen  使用占比非常小就进行了一次 CMS GC?
 
 
触发条件 
CMS GC 在实现上分成 foreground collector 和 background collector。foreground  collector 相对比较简单,background collector 比较复杂,情况比较多。 
下面我们从 foreground collector 和 background collector 分别来说明他们的触发条件: 
说明:本文内容是基于 JDK 8 
说明:本文仅涉及 CMS GC 的触发条件,至于算法的具体过程,以及什么时候进行 MSC(mark sweep  compact)不在本文范围 
foreground collector 
foreground collector 触发条件比较简单,一般是遇到对象分配但空间不够,就会直接触发 GC,来立即进行空间回收。采用的算法是 mark  sweep,不压缩。 
background collector 
说明 background collector 的触发条件之前,先来说下 background collector 的流程,它是通过 CMS  后台线程不断的去扫描,过程中主要是判断是否符合 background collector 的触发条件,一旦有符合的情况,就会进行一次 background 的  collect。 
- void ConcurrentMarkSweepThread::run() {  
 - ...//省略  
 - while (!_should_terminate) {  
 - sleepBeforeNextCycle();  
 - if (_should_terminate) break;  
 - GCCause::Cause cause = _collector->_full_gc_requested ?  
 - _collector->_full_gc_cause : GCCause::_cms_concurrent_mark;  
 - _collector->collect_in_background(false, cause);  
 - }  
 - ...//省略  
 - }  
 
  
每次扫描过程中,先等 CMSWaitDuration 时间,然后再去进行一次 shouldConcurrentCollect 判断,看是否满足 CMS  background collector 的触发条件。CMSWaitDuration 默认时间是 2s(经常会有业务遇到频繁的 CMS GC,注意看每次 CMS  GC 之间的时间间隔,如果是 2s,那基本就可以断定是 CMS 的 background collector)。 
- void ConcurrentMarkSweepThread::sleepBeforeNextCycle() {  
 - while (!_should_terminate) {  
 - if (CMSIncrementalMode) {  
 - icms_wait();  
 - if(CMSWaitDuration >= 0) {  
 - // Wait until the next synchronous GC, a concurrent full gc  
 - // request or a timeout, whichever is earlier.  
 - wait_on_cms_lock_for_scavenge(CMSWaitDuration);  
 - }  
 - return;  
 - } else {  
 - if(CMSWaitDuration >= 0) {  
 - // Wait until the next synchronous GC, a concurrent full gc  
 - // request or a timeout, whichever is earlier.  
 - wait_on_cms_lock_for_scavenge(CMSWaitDuration);  
 - } else {  
 - // Wait until any cms_lock event or check interval not to call shouldConcurrentCollect permanently  
 - wait_on_cms_lock(CMSCheckInterval);  
 - }  
 - }  
 - // Check if we should start a CMS collection cycle  
 - if (_collector->shouldConcurrentCollect()) {  
 - return;  
 - }  
 - // .. collection criterion not yet met, let's go back  
 - // and wait some more  
 - }  
 - }  
 
  
那 shouldConcurrentCollect() 方法中都有哪些条件呢? 
- bool CMSCollector::shouldConcurrentCollect() { 
 - // 第一种触发情况 
 - if (_full_gc_requested) { 
 - if (Verbose && PrintGCDetails) { 
 - gclog_or_tty->print_cr("CMSCollector: collect because of explicit " 
 - " gc request (or gc_locker)"); 
 - } 
 - return true; 
 - } 
 - // For debugging purposes, change the type of collection. 
 - // If the rotation is not on the concurrent collection 
 - // type, don't start a concurrent collection. 
 - NOT_PRODUCT( 
 - if (RotateCMSCollectionTypes && 
 - (_cmsGen->debug_collection_type() != 
 - ConcurrentMarkSweepGeneration::Concurrent_collection_type)) { 
 - assert(_cmsGen->debug_collection_type() != 
 - ConcurrentMarkSweepGeneration::Unknown_collection_type, 
 - "Bad cms collection type"); 
 - return false; 
 - } 
 - ) 
 - FreelistLocker x(this); 
 - // ------------------------------------------------------------------ 
 - // Print out lots of information which affects the initiation of 
 - // a collection. 
 - if (PrintCMSInitiationStatistics && stats().valid()) { 
 - gclog_or_tty->print("CMSCollector shouldConcurrentCollect: "); 
 - gclog_or_tty->stamp(); 
 - gclog_or_tty->print_cr(""); 
 - stats().print_on(gclog_or_tty); 
 - gclog_or_tty->print_cr("time_until_cms_gen_full %3.7f", 
 - stats().time_until_cms_gen_full()); 
 - gclog_or_tty->print_cr("free="SIZE_FORMAT, _cmsGen->free()); 
 - gclog_or_tty->print_cr("contiguous_available="SIZE_FORMAT, 
 - _cmsGen->contiguous_available()); 
 - gclog_or_tty->print_cr("promotion_rate=%g", stats().promotion_rate()); 
 - gclog_or_tty->print_cr("cms_allocation_rate=%g", stats().cms_allocation_rate()); 
 - gclog_or_tty->print_cr("occupancy=%3.7f", _cmsGen->occupancy()); 
 - gclog_or_tty->print_cr("initiatingOccupancy=%3.7f", _cmsGen->initiating_occupancy()); 
 - gclog_or_tty->print_cr("metadata initialized %d", 
 - MetaspaceGC::should_concurrent_collect()); 
 - } 
 - // ------------------------------------------------------------------ 
 - // 第二种触发情况 
 - // If the estimated time to complete a cms collection (cms_duration()) 
 - // is less than the estimated time remaining until the cms generation 
 - // is full, start a collection. 
 - if (!UseCMSInitiatingOccupancyOnly) { 
 - if (stats().valid()) { 
 - if (stats().time_until_cms_start() == 0.0) { 
 - return true; 
 - } 
 - } else { 
 - // We want to conservatively collect somewhat early in order 
 - // to try and "bootstrap" our CMS/promotion statistics; 
 - // this branch will not fire after the first successful CMS 
 - // collection because the stats should then be valid. 
 - if (_cmsGen->occupancy() >= _bootstrap_occupancy) { 
 - if (Verbose && PrintGCDetails) { 
 - gclog_or_tty->print_cr( 
 - " CMSCollector: collect for bootstrapping statistics:" 
 - " occupancy = %f, boot occupancy = %f", _cmsGen->occupancy(), 
 - _bootstrap_occupancy); 
 - } 
 - return true; 
 - } 
 - } 
 - } 
 - // 第三种触发情况 
 - // Otherwise, we start a collection cycle if 
 - // old gen want a collection cycle started. Each may use 
 - // an appropriate criterion for making this decision. 
 - // XXX We need to make sure that the gen expansion 
 - // criterion dovetails well with this. XXX NEED TO FIX THIS 
 - if (_cmsGen->should_concurrent_collect()) { 
 - if (Verbose && PrintGCDetails) { 
 - gclog_or_tty->print_cr("CMS old gen initiated"); 
 - } 
 - return true; 
 - } 
 - // 第四种触发情况 
 - // We start a collection if we believe an incremental collection may fail; 
 - // this is not likely to be productive in practice because it's probably too 
 - // late anyway. 
 - GenCollectedHeap* gch = GenCollectedHeap::heap(); 
 - assert(gch->collector_policy()->is_two_generation_policy(), 
 - "You may want to check the correctness of the following"); 
 - if (gch->incremental_collection_will_fail(true /* consult_young */)) { 
 - if (Verbose && PrintGCDetails) { 
 - gclog_or_tty->print("CMSCollector: collect because incremental collection will fail "); 
 - } 
 - return true; 
 - } 
 - // 第五种触发情况 
 - if (MetaspaceGC::should_concurrent_collect()) { 
 - if (Verbose && PrintGCDetails) { 
 - gclog_or_tty->print("CMSCollector: collect for metadata allocation "); 
 - } 
 - return true; 
 - } 
 - return false; 
 - } 
 
  
上述代码可知,从大类上分, background collector 一共有 5 种触发情况: 
1.是否是并行 Full GC 
                                                (编辑:91站长网) 
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! 
                     |