自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

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

[[267920]]

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

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

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

觸發(fā)條件

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

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

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

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

foreground collector

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

background collector

說明 background collector 的觸發(fā)條件之前,先來說下 background collector 的流程,它是通過 CMS 后臺(tái)線程不斷的去掃描,過程中主要是判斷是否符合 background collector 的觸發(fā)條件,一旦有符合的情況,就會(huì)進(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 時(shí)間,然后再去進(jìn)行一次 shouldConcurrentCollect 判斷,看是否滿足 CMS background collector 的觸發(fā)條件。CMSWaitDuration 默認(rèn)時(shí)間是 2s(經(jīng)常會(huì)有業(yè)務(wù)遇到頻繁的 CMS GC,注意看每次 CMS GC 之間的時(shí)間間隔,如果是 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ù),這時(shí)會(huì)觸發(fā)一次 background collector。

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

判斷邏輯是,如果預(yù)測(cè) CMS GC 完成所需要的時(shí)間大于預(yù)計(jì)的老年代將要填滿的時(shí)間,則進(jìn)行 GC。 這些判斷是需要基于歷史的 CMS GC 統(tǒng)計(jì)指標(biāo),然而,***次 CMS GC 時(shí),統(tǒng)計(jì)數(shù)據(jù)還沒有形成,是無效的,這時(shí)會(huì)跟據(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%。或許你已經(jīng)遇到過類似案例,在沒有配置 UseCMSInitiatingOccupancyOnly 時(shí),發(fā)現(xiàn)老年代占比到 50% 就進(jìn)行了一次 CMS GC,當(dāng)時(shí)的你或許還一頭霧水呢。

  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 時(shí)(注意,默認(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)閷?duì)象分配空間而進(jìn)行擴(kuò)容,且成功分配空間,這時(shí)會(huì)考慮進(jìn)行一次 CMS GC;
  • 根據(jù) CMS Gen 空閑鏈判斷,這里有點(diǎn)復(fù)雜,目前也沒整清楚,好在按照默認(rèn)配置其實(shí)這里返回的是 false,所以默認(rèn)是不用考慮這種觸發(fā)條件了。

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

什么意思呢?兩代的 GC 體系中,主要指的是 Young GC 是否會(huì)失敗。如果 Young GC 已經(jīng)失敗或者可能會(huì)失敗,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()); 

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

!getgen(0)->collectionattemptissafe() 指的是新生代晉升是否安全。 通過判斷當(dāng)前 Old Gen 剩余的空間大小是否足夠容納 Young GC 晉升的對(duì)象大小。 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)志,這個(gè)標(biāo)志在 meta space 進(jìn)行擴(kuò)容前如果配置了 CMSClassUnloadingEnabled 參數(shù)時(shí),會(huì)進(jìn)行設(shè)置。這種情況下就會(huì)進(jìn)行一次 CMS GC。因此經(jīng)常會(huì)有應(yīng)用啟動(dòng)不久,Old Gen 空間占比還很小的情況下,進(jìn)行了一次 CMS GC,讓你很莫名其妙,其實(shí)就是這個(gè)原因?qū)е碌摹?/p>

總結(jié)

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

責(zé)任編輯:武曉燕 來源: 占小狼的博客
相關(guān)推薦

2020-11-18 08:15:39

TypeScript設(shè)計(jì)模式

2013-06-26 09:01:22

JVMJava

2010-10-13 13:20:04

命令行標(biāo)志JVM

2013-06-26 11:23:40

JVM命令行JavaJVM

2014-04-03 10:16:44

JavaScriptCSS

2023-11-30 08:32:31

OpenFeign工具

2023-09-08 08:23:29

Servlet程序MVC

2023-08-28 07:02:10

2021-08-30 07:49:33

索引ICP Mysql

2018-09-02 15:43:56

Python代碼編程語言

2013-05-20 10:14:42

軟件工具項(xiàng)目工具開發(fā)工具

2020-04-26 10:32:58

Kubernetes集群Pod

2019-08-29 14:29:42

JVM內(nèi)存 Java

2020-07-13 07:58:18

5G網(wǎng)絡(luò)技術(shù)

2023-11-15 08:22:42

Java開發(fā)小技巧

2022-08-11 08:46:23

索引數(shù)據(jù)結(jié)構(gòu)

2023-12-13 08:28:07

2021-08-18 16:47:10

5G 5G網(wǎng)絡(luò)5G商用

2022-02-15 20:08:41

JDKJavaWindows

2022-07-05 08:34:22

虛擬機(jī)JavaJVM
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)