日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
JVM發(fā)生CMSGC的5種情況,你知道的肯定不全!

JVM發(fā)生CMS GC的 5 種情況,你知道的肯定不全!

作者:滌生YQ 2019-06-12 15:57:44

云計(jì)算

虛擬化 經(jīng)常有同學(xué)會問,為啥我的應(yīng)用 Old Gen 的使用占比沒達(dá)到 CMSInitiatingOccupancyFraction 參數(shù)配置的閾值,就觸發(fā)了 CMS GC,表示很莫名奇妙,不知道問題出在哪?

成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供常熟網(wǎng)站建設(shè)、常熟做網(wǎng)站、常熟網(wǎng)站設(shè)計(jì)、常熟網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、常熟企業(yè)網(wǎng)站模板建站服務(wù),十年常熟做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

經(jīng)常有同學(xué)會問,為啥我的應(yīng)用 Old Gen 的使用占比沒達(dá)到 CMSInitiatingOccupancyFraction 參數(shù)配置的閾值,就觸發(fā)了 CMS GC,表示很莫名奇妙,不知道問題出在哪?

其實(shí) CMS GC 的觸發(fā)條件非常多,不只是 CMSInitiatingOccupancyFraction 閾值觸發(fā)這么簡單。本文通過源碼全面梳理了觸發(fā) CMS GC 的條件,盡可能的幫你了解平時遇到的奇奇怪怪的 CMS GC 問題。

先拋出一些問題,來吸引你的注意力。

  • 為什么 Old Gen 使用占比僅 50% 就進(jìn)行了一次 CMS GC?
  • Metaspace 的使用也會觸發(fā) CMS GC 嗎?
  • 為什么 Old Gen 使用占比非常小就進(jìn)行了一次 CMS GC?

觸發(fā)條件

CMS GC 在實(shí)現(xiàn)上分成 foreground collector 和 background collector。foreground collector 相對比較簡單,background collector 比較復(fù)雜,情況比較多。

下面我們從 foreground collector 和 background collector 分別來說明他們的觸發(fā)條件:

說明:本文內(nèi)容是基于 JDK 8

說明:本文僅涉及 CMS GC 的觸發(fā)條件,至于算法的具體過程,以及什么時候進(jìn)行 MSC(mark sweep compact)不在本文范圍。

foreground collector

foreground collector 觸發(fā)條件比較簡單,一般是遇到對象分配但空間不夠,就會直接觸發(fā) GC,來立即進(jìn)行空間回收。采用的算法是 mark sweep,不壓縮。

background collector

說明 background collector 的觸發(fā)條件之前,先來說下 background collector 的流程,它是通過 CMS 后臺線程不斷的去掃描,過程中主要是判斷是否符合 background collector 的觸發(fā)條件,一旦有符合的情況,就會進(jìn)行一次 background 的 collect。

  
 
 
  1. void ConcurrentMarkSweepThread::run() {  
  2. ...//省略  
  3. while (!_should_terminate) {  
  4. sleepBeforeNextCycle();  
  5. if (_should_terminate) break;  
  6. GCCause::Cause cause = _collector->_full_gc_requested ?  
  7. _collector->_full_gc_cause : GCCause::_cms_concurrent_mark;  
  8. _collector->collect_in_background(false, cause);  
  9. }  
  10. ...//省略  
  11. }  

每次掃描過程中,先等 CMSWaitDuration 時間,然后再去進(jìn)行一次 shouldConcurrentCollect 判斷,看是否滿足 CMS background collector 的觸發(fā)條件。CMSWaitDuration 默認(rèn)時間是 2s(經(jīng)常會有業(yè)務(wù)遇到頻繁的 CMS GC,注意看每次 CMS GC 之間的時間間隔,如果是 2s,那基本就可以斷定是 CMS 的 background collector)。

  
 
 
  1. void ConcurrentMarkSweepThread::sleepBeforeNextCycle() {  
  2. while (!_should_terminate) {  
  3. if (CMSIncrementalMode) {  
  4. icms_wait();  
  5. if(CMSWaitDuration >= 0) {  
  6. // Wait until the next synchronous GC, a concurrent full gc  
  7. // request or a timeout, whichever is earlier.  
  8. wait_on_cms_lock_for_scavenge(CMSWaitDuration);  
  9. }  
  10. return;  
  11. } else {  
  12. if(CMSWaitDuration >= 0) {  
  13. // Wait until the next synchronous GC, a concurrent full gc  
  14. // request or a timeout, whichever is earlier.  
  15. wait_on_cms_lock_for_scavenge(CMSWaitDuration);  
  16. } else {  
  17. // Wait until any cms_lock event or check interval not to call shouldConcurrentCollect permanently  
  18. wait_on_cms_lock(CMSCheckInterval);  
  19. }  
  20. }  
  21. // Check if we should start a CMS collection cycle  
  22. if (_collector->shouldConcurrentCollect()) {  
  23. return;  
  24. }  
  25. // .. collection criterion not yet met, let's go back  
  26. // and wait some more  
  27. }  
  28. }  

那 shouldConcurrentCollect() 方法中都有哪些條件呢?

  
 
 
  1. bool CMSCollector::shouldConcurrentCollect() { 
  2. // ***種觸發(fā)情況 
  3. if (_full_gc_requested) { 
  4. if (Verbose && PrintGCDetails) { 
  5. gclog_or_tty->print_cr("CMSCollector: collect because of explicit " 
  6. " gc request (or gc_locker)"); 
  7. return true; 
  8. // For debugging purposes, change the type of collection. 
  9. // If the rotation is not on the concurrent collection 
  10. // type, don't start a concurrent collection. 
  11. NOT_PRODUCT( 
  12. if (RotateCMSCollectionTypes && 
  13. (_cmsGen->debug_collection_type() != 
  14. ConcurrentMarkSweepGeneration::Concurrent_collection_type)) { 
  15. assert(_cmsGen->debug_collection_type() != 
  16. ConcurrentMarkSweepGeneration::Unknown_collection_type, 
  17. "Bad cms collection type"); 
  18. return false; 
  19. FreelistLocker x(this); 
  20. // ------------------------------------------------------------------ 
  21. // Print out lots of information which affects the initiation of 
  22. // a collection. 
  23. if (PrintCMSInitiationStatistics && stats().valid()) { 
  24. gclog_or_tty->print("CMSCollector shouldConcurrentCollect: "); 
  25. gclog_or_tty->stamp(); 
  26. gclog_or_tty->print_cr(""); 
  27. stats().print_on(gclog_or_tty); 
  28. gclog_or_tty->print_cr("time_until_cms_gen_full %3.7f", 
  29. stats().time_until_cms_gen_full()); 
  30. gclog_or_tty->print_cr("free="SIZE_FORMAT, _cmsGen->free()); 
  31. gclog_or_tty->print_cr("contiguous_available="SIZE_FORMAT, 
  32. _cmsGen->contiguous_available()); 
  33. gclog_or_tty->print_cr("promotion_rate=%g", stats().promotion_rate()); 
  34. gclog_or_tty->print_cr("cms_allocation_rate=%g", stats().cms_allocation_rate()); 
  35. gclog_or_tty->print_cr("occupancy=%3.7f", _cmsGen->occupancy()); 
  36. gclog_or_tty->print_cr("initiatingOccupancy=%3.7f", _cmsGen->initiating_occupancy()); 
  37. gclog_or_tty->print_cr("metadata initialized %d", 
  38. MetaspaceGC::should_concurrent_collect()); 
  39. // ------------------------------------------------------------------ 
  40. // 第二種觸發(fā)情況 
  41. // If the estimated time to complete a cms collection (cms_duration()) 
  42. // is less than the estimated time remaining until the cms generation 
  43. // is full, start a collection. 
  44. if (!UseCMSInitiatingOccupancyOnly) { 
  45. if (stats().valid()) { 
  46. if (stats().time_until_cms_start() == 0.0) { 
  47. return true; 
  48. } else { 
  49. // We want to conservatively collect somewhat early in order 
  50. // to try and "bootstrap" our CMS/promotion statistics; 
  51. // this branch will not fire after the first successful CMS 
  52. // collection because the stats should then be valid. 
  53. if (_cmsGen->occupancy() >= _bootstrap_occupancy) { 
  54. if (Verbose && PrintGCDetails) { 
  55. gclog_or_tty->print_cr( 
  56. " CMSCollector: collect for bootstrapping statistics:" 
  57. " occupancy = %f, boot occupancy = %f", _cmsGen->occupancy(), 
  58. _bootstrap_occupancy); 
  59. return true; 
  60. // 第三種觸發(fā)情況 
  61. // Otherwise, we start a collection cycle if 
  62. // old gen want a collection cycle started. Each may use 
  63. // an appropriate criterion for making this decision. 
  64. // XXX We need to make sure that the gen expansion 
  65. // criterion dovetails well with this. XXX NEED TO FIX THIS 
  66. if (_cmsGen->should_concurrent_collect()) { 
  67. if (Verbose && PrintGCDetails) { 
  68. gclog_or_tty->print_cr("CMS old gen initiated"); 
  69. return true; 
  70. // 第四種觸發(fā)情況 
  71. // We start a collection if we believe an incremental collection may fail; 
  72. // this is not likely to be productive in practice because it's probably too 
  73. // late anyway. 
  74. GenCollectedHeap* gch = GenCollectedHeap::heap(); 
  75. assert(gch->collector_policy()->is_two_generation_policy(), 
  76. "You may want to check the correctness of the following"); 
  77. if (gch->incremental_collection_will_fail(true /* consult_young */)) { 
  78. if (Verbose && PrintGCDetails) { 
  79. gclog_or_tty->print("CMSCollector: collect because incremental collection will fail "); 
  80. return true; 
  81. // 第五種觸發(fā)情況 
  82. if (MetaspaceGC::should_concurrent_collect()) { 
  83. if (Verbose && PrintGCDetails) { 
  84. gclog_or_tty->print("CMSCollector: collect for metadata allocation "); 
  85. return true; 
  86. return false; 

上述代碼可知,從大類上分, background collector 一共有 5 種觸發(fā)情況:

1.是否是并行 Full GC

指的是在 GC cause 是 gclocker 且配置了 GCLockerInvokesConcurrent 參數(shù), 或者 GC cause 是javalangsystemgc(就是 System.gc()調(diào)用)and 且配置了 ExplicitGCInvokesConcurrent 參數(shù),這時會觸發(fā)一次 background collector。

2.根據(jù)統(tǒng)計(jì)數(shù)據(jù)動態(tài)計(jì)算(僅未配置 UseCMSInitiatingOccupancyOnly 時) 未配置 UseCMSInitiatingOccupancyOnly 時,會根據(jù)統(tǒng)計(jì)數(shù)據(jù)動態(tài)判斷是否需要進(jìn)行一次 CMS GC。

判斷邏輯是,如果預(yù)測 CMS GC 完成所需要的時間大于預(yù)計(jì)的老年代將要填滿的時間,則進(jìn)行 GC。 這些判斷是需要基于歷史的 CMS GC 統(tǒng)計(jì)指標(biāo),然而,***次 CMS GC 時,統(tǒng)計(jì)數(shù)據(jù)還沒有形成,是無效的,這時會跟據(jù) Old Gen 的使用占比來判斷是否要進(jìn)行 GC。

  
 
 
  1. if (!UseCMSInitiatingOccupancyOnly) { 
  2. if (stats().valid()) { 
  3. if (stats().time_until_cms_start() == 0.0) { 
  4. return true; 
  5. } else { 
  6. // We want to conservatively collect somewhat early in order 
  7. // to try and "bootstrap" our CMS/promotion statistics; 
  8. // this branch will not fire after the first successful CMS 
  9. // collection because the stats should then be valid. 
  10. if (_cmsGen->occupancy() >= _bootstrap_occupancy) { 
  11. if (Verbose && PrintGCDetails) { 
  12. gclog_or_tty->print_cr( 
  13. " CMSCollector: collect for bootstrapping statistics:" 
  14. " occupancy = %f, boot occupancy = %f", _cmsGen->occupancy(), 
  15. _bootstrap_occupancy); 
  16. return true; 

那占多少比率,開始回收呢?(也就是 bootstrapoccupancy 的值是多少呢?) 答案是 50%?;蛟S你已經(jīng)遇到過類似案例,在沒有配置 UseCMSInitiatingOccupancyOnly 時,發(fā)現(xiàn)老年代占比到 50% 就進(jìn)行了一次 CMS GC,當(dāng)時的你或許還一頭霧水呢。

  
 
 
  1. _bootstrap_occupancy = ((double)CMSBootstrapOccupancy)/(double)100; 
  2. //參數(shù)默認(rèn)值 
  3. product(uintx, CMSBootstrapOccupancy, 50, 
  4. "Percentage CMS generation occupancy at which to initiate CMS collection for bootstrapping collection stats") 

3.根據(jù) Old Gen 情況判斷

  
 
 
  1. bool ConcurrentMarkSweepGeneration::should_concurrent_collect() const { 
  2. assert_lock_strong(freelistLock()); 
  3. if (occupancy() > initiating_occupancy()) { 
  4. if (PrintGCDetails && Verbose) { 
  5. gclog_or_tty->print(" %s: collect because of occupancy %f / %f ", 
  6. short_name(), occupancy(), initiating_occupancy()); 
  7. return true; 
  8. if (UseCMSInitiatingOccupancyOnly) { 
  9. return false; 
  10. if (expansion_cause() == CMSExpansionCause::_satisfy_allocation) { 
  11. if (PrintGCDetails && Verbose) { 
  12. gclog_or_tty->print(" %s: collect because expanded for allocation ", 
  13. short_name()); 
  14. return true; 
  15. if (_cmsSpace->should_concurrent_collect()) { 
  16. if (PrintGCDetails && Verbose) { 
  17. gclog_or_tty->print(" %s: collect because cmsSpace says so ", 
  18. short_name()); 
  19. return true; 
  20. return false; 

從源碼上看,這里主要分成兩類: (a) Old Gen 空間使用占比情況與閾值比較,如果大于閾值則進(jìn)行 CMS GC 也就是"occupancy() > initiatingoccupancy()",occupancy 毫無疑問是 Old Gen 當(dāng)前空間的使用占比,而 initiatingoccupancy 是多少呢?

  
 
 
  1. _cmsGen ->init_initiating_occupancy(CMSInitiatingOccupancyFraction, CMSTriggerRatio); 
  2. ... 
  3. void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, uintx tr) { 
  4. assert(io <= 100 && tr <= 100, "Check the arguments"); 
  5. if (io >= 0) { 
  6. _initiating_occupancy = (double)io / 100.0; 
  7. } else { 
  8. _initiating_occupancy = ((100 - MinHeapFreeRatio) + 
  9. (double)(tr * MinHeapFreeRatio) / 100.0) 
  10. / 100.0; 

可以看到當(dāng) CMSInitiatingOccupancyFraction 參數(shù)配置值大于 0,就是 “io / 100.0”;

當(dāng) CMSInitiatingOccupancyFraction 參數(shù)配置值小于 0 時(注意,默認(rèn)是 -1),是 “((100 - MinHeapFreeRatio) + (double)(tr * MinHeapFreeRatio) / 100.0) / 100.0”,這到底是多少呢?是 92%,這里就不貼出具體的計(jì)算過程了,或許你已經(jīng)在某些書或者博客中了解過,CMSInitiatingOccupancyFraction 沒有配置,就是 92,但是其實(shí) CMSInitiatingOccupancyFraction 沒有配置是 -1,所以閾值取后者 92%,并不是 CMSInitiatingOccupancyFraction 的值是 92。

(b) 接下來沒有配置 UseCMSInitiatingOccupancyOnly 的情況

這里也分成有兩小類情況:

  • 當(dāng) Old Gen 剛因?yàn)閷ο蠓峙淇臻g而進(jìn)行擴(kuò)容,且成功分配空間,這時會考慮進(jìn)行一次 CMS GC;
  • 根據(jù) CMS Gen 空閑鏈判斷,這里有點(diǎn)復(fù)雜,目前也沒整清楚,好在按照默認(rèn)配置其實(shí)這里返回的是 false,所以默認(rèn)是不用考慮這種觸發(fā)條件了。

4.根據(jù)增量 GC 是否可能會失敗(悲觀策略)

什么意思呢?兩代的 GC 體系中,主要指的是 Young GC 是否會失敗。如果 Young GC 已經(jīng)失敗或者可能會失敗,JVM 就認(rèn)為需要進(jìn)行一次 CMS GC。

  
 
 
  1. bool incremental_collection_will_fail(bool consult_young) { 
  2. // Assumes a 2-generation system; the first disjunct remembers if an 
  3. // incremental collection failed, even when we thought (second disjunct) 
  4. // that it would not. 
  5. assert(heap()->collector_policy()->is_two_generation_policy(), 
  6. "the following definition may not be suitable for an n(>2)-generation system"); 
  7. return incremental_collection_failed() || 
  8. (consult_young && !get_gen(0)->collection_attempt_is_safe()); 

我們看兩個判斷條件,“incrementalcollectionfailed()” 和 “!getgen(0)->collectionattemptissafe()” incrementalcollectionfailed() 這里指的是 Young GC 已經(jīng)失敗,至于為什么會失敗一般是因?yàn)?Old Gen 沒有足夠的空間來容納晉升的對象。

!getgen(0)->collectionattemptissafe() 指的是新生代晉升是否安全。 通過判斷當(dāng)前 Old Gen 剩余的空間大小是否足夠容納 Young GC 晉升的對象大小。 Young GC 到底要晉升多少是無法提前知道的,因此,這里通過統(tǒng)計(jì)平均每次 Young GC 晉升的大小和當(dāng)前 Young GC 可能晉升的***大小來進(jìn)行比較。

  
 
 
  1. //av_promo 是平均每次 YoungGC 晉升的大小,max_promotion_in_bytes 是當(dāng)前可能的***晉升大小( eden+from 當(dāng)前使用空間的大小) 
  2. bool res = (available >= av_promo) || (available >= max_promotion_in_bytes); 

5.根據(jù) meta space 情況判斷

這里主要看 metaspace 的 shouldconcurrent_collect 標(biāo)志,這個標(biāo)志在 meta space 進(jìn)行擴(kuò)容前如果配置了 CMSClassUnloadingEnabled 參數(shù)時,會進(jìn)行設(shè)置。這種情況下就會進(jìn)行一次 CMS GC。因此經(jīng)常會有應(yīng)用啟動不久,Old Gen 空間占比還很小的情況下,進(jìn)行了一次 CMS GC,讓你很莫名其妙,其實(shí)就是這個原因?qū)е碌摹?/p>

總結(jié)

本文梳理了 CMS GC 的 foreground collector 和 background collector 的觸發(fā)條件,foreground collector 的觸發(fā)條件相對來說比較簡單,而 background collector 的觸發(fā)條件比較多,分成 5 大種情況,各大種情況種還有一些小的觸發(fā)分支。尤其是在沒有配置 UseCMSInitiatingOccupancyOnly 參數(shù)的情況下,會多出很多種觸發(fā)可能,一般在生產(chǎn)環(huán)境是強(qiáng)烈建議配置 UseCMSInitiatingOccupancyOnly 參數(shù),以便于能夠比較確定的執(zhí)行 CMS GC,另外,也方便排查 GC 原因。


分享題目:JVM發(fā)生CMSGC的5種情況,你知道的肯定不全!
文章來源:http://m.5511xx.com/article/cdepdpj.html