成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

云計算 虛擬化
經常有同學會問,為啥我的應用 Old Gen 的使用占比沒達到 CMSInitiatingOccupancyFraction 參數配置的閾值,就觸發了 CMS GC,表示很莫名奇妙,不知道問題出在哪?

經常有同學會問,為啥我的應用 Old Gen 的使用占比沒達到 CMSInitiatingOccupancyFraction 參數配置的閾值,就觸發了 CMS GC,表示很莫名奇妙,不知道問題出在哪?

[[267920]]

其實 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。

  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 時間,然后再去進行一次 shouldConcurrentCollect 判斷,看是否滿足 CMS background collector 的觸發條件。CMSWaitDuration 默認時間是 2s(經常會有業務遇到頻繁的 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. // ***種觸發情況 
  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. // 第二種觸發情況 
  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. // 第三種觸發情況 
  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. // 第四種觸發情況 
  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. // 第五種觸發情況 
  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 種觸發情況:

1.是否是并行 Full GC

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

2.根據統計數據動態計算(僅未配置 UseCMSInitiatingOccupancyOnly 時) 未配置 UseCMSInitiatingOccupancyOnly 時,會根據統計數據動態判斷是否需要進行一次 CMS GC。

判斷邏輯是,如果預測 CMS GC 完成所需要的時間大于預計的老年代將要填滿的時間,則進行 GC。 這些判斷是需要基于歷史的 CMS GC 統計指標,然而,***次 CMS GC 時,統計數據還沒有形成,是無效的,這時會跟據 Old Gen 的使用占比來判斷是否要進行 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%。或許你已經遇到過類似案例,在沒有配置 UseCMSInitiatingOccupancyOnly 時,發現老年代占比到 50% 就進行了一次 CMS GC,當時的你或許還一頭霧水呢。

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

3.根據 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 空間使用占比情況與閾值比較,如果大于閾值則進行 CMS GC 也就是"occupancy() > initiatingoccupancy()",occupancy 毫無疑問是 Old Gen 當前空間的使用占比,而 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; 

可以看到當 CMSInitiatingOccupancyFraction 參數配置值大于 0,就是 “io / 100.0”;

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

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

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

  • 當 Old Gen 剛因為對象分配空間而進行擴容,且成功分配空間,這時會考慮進行一次 CMS GC;
  • 根據 CMS Gen 空閑鏈判斷,這里有點復雜,目前也沒整清楚,好在按照默認配置其實這里返回的是 false,所以默認是不用考慮這種觸發條件了。

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

什么意思呢?兩代的 GC 體系中,主要指的是 Young GC 是否會失敗。如果 Young GC 已經失敗或者可能會失敗,JVM 就認為需要進行一次 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 已經失敗,至于為什么會失敗一般是因為 Old Gen 沒有足夠的空間來容納晉升的對象。

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

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

5.根據 meta space 情況判斷

這里主要看 metaspace 的 shouldconcurrent_collect 標志,這個標志在 meta space 進行擴容前如果配置了 CMSClassUnloadingEnabled 參數時,會進行設置。這種情況下就會進行一次 CMS GC。因此經常會有應用啟動不久,Old Gen 空間占比還很小的情況下,進行了一次 CMS GC,讓你很莫名其妙,其實就是這個原因導致的。

總結

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

責任編輯:武曉燕 來源: 占小狼的博客
相關推薦

2020-11-18 08:15:39

TypeScript設計模式

2013-06-26 11:23:40

JVM命令行JavaJVM

2010-10-13 13:20:04

命令行標志JVM

2013-06-26 09:01:22

JVMJava

2023-11-30 08:32:31

OpenFeign工具

2014-04-03 10:16:44

JavaScriptCSS

2023-09-08 08:23:29

Servlet程序MVC

2013-05-20 10:14:42

軟件工具項目工具開發工具

2018-09-02 15:43:56

Python代碼編程語言

2021-08-30 07:49:33

索引ICP Mysql

2023-08-28 07:02:10

2023-11-15 08:22:42

Java開發小技巧

2020-04-26 10:32:58

Kubernetes集群Pod

2020-07-13 07:58:18

5G網絡技術

2019-08-29 14:29:42

JVM內存 Java

2023-12-13 08:28:07

2014-12-17 09:27:41

開源PaaS

2022-08-11 08:46:23

索引數據結構

2021-08-18 16:47:10

5G 5G網絡5G商用

2022-02-15 20:08:41

JDKJavaWindows
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 宅女噜噜66国产精品观看免费 | 浴室洗澡偷拍一区二区 | 色视频在线免费观看 | 亚洲综合色视频在线观看 | 国产高清免费在线 | 亚洲一区高清 | 伊人99 | 精品一区精品二区 | 亚州精品天堂中文字幕 | 夜夜爽99久久国产综合精品女不卡 | 国产欧美一级二级三级在线视频 | 亚洲精品v日韩精品 | 成人欧美一区二区三区在线播放 | 欧美一区二区三区 | 成人在线观看免费 | 91porn成人精品 | 亚洲欧美国产精品久久 | 国产精品免费在线 | 国产欧美日韩精品一区 | 欧美极品在线播放 | 色av一区二区 | 免费的一级视频 | 日本不卡一区二区三区在线观看 | 天堂资源最新在线 | 国产精华一区 | 久久精品亚洲欧美日韩久久 | 精品日韩一区 | 日韩成人在线观看 | 欧美日韩专区 | 亚洲狠狠| 福利精品 | 一区在线视频 | 96av麻豆蜜桃一区二区 | 免费在线观看av片 | 亚洲一区二区国产 | 中文字幕在线观看一区 | 欧美一区二区三区在线 | 免费一级黄色 | 精品久久久久久 | 欧美性a视频 | 亚洲午夜精品一区二区三区 |