譯文-JVM中CMS收集器

原文出處:Concurrent Mark and Sweephtml

這種垃圾收集器的官方名稱是「Mostly Concurrent Mark and Sweep Garbage Collector」。它在Yong代使用
並行,「stop-the-world」、標記複製算法,在Old代使用併發標記清除算法。java

設計這種算法目的是,在回收Old代的時候去避免長時間停頓。他的實現分爲兩方面。第一,它不清楚Old代,而是使用空閒列表管理回收空間。第二,它在標記清除期間的大部分工做和應用程序併發執行。這意味着執行這些階段,收集器不會刻意停頓應用線程。應當注意的是,它依然和應用程序搶佔CPU時間片。默認的,這種算法的使用的線程數等於你的機器物理核心數的¼。算法

你在命令行經過以下圖所示的選項顯式指定這個收集器:數據結構

java -XX:+UseConcMarkSweepGC

若是你有意向的話,在多核機器上使用這樣的組合是個不錯的選擇。應用用戶能夠明顯感知個別GC階段停頓時長減小帶來的變化,快速響應給他們帶來更好用戶體驗。大多時候,GC消耗至少幾個CPU資源,而不是執行你的應用代碼。在單核應用中CMS性能一般低於Parallel。併發

就拿上面的GC算法來講,讓咱們看一下這種算法在實踐應用當中Minor GC和Major GC階段如何工做:oracle

2015-05-26T16:23:07.219-0200: 64.322: [GC (Allocation Failure) 64.322: [ParNew: 613404K->68068K(613440K), 0.1020465 secs] 10885349K->10880154K(12514816K), 0.1021309 secs] [Times: user=0.78 sys=0.01, real=0.11 secs]
2015-05-26T16:23:07.321-0200: 64.425: [GC (CMS Initial Mark) [1 CMS-initial-mark: 10812086K(11901376K)] 10887844K(12514816K), 0.0001997 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2015-05-26T16:23:07.321-0200: 64.425: [CMS-concurrent-mark-start]
2015-05-26T16:23:07.357-0200: 64.460: [CMS-concurrent-mark: 0.035/0.035 secs] [Times: user=0.07 sys=0.00, real=0.03 secs]
2015-05-26T16:23:07.357-0200: 64.460: [CMS-concurrent-preclean-start]
2015-05-26T16:23:07.373-0200: 64.476: [CMS-concurrent-preclean: 0.016/0.016 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
2015-05-26T16:23:07.373-0200: 64.476: [CMS-concurrent-abortable-preclean-start]
2015-05-26T16:23:08.446-0200: 65.550: [CMS-concurrent-abortable-preclean: 0.167/1.074 secs] [Times: user=0.20 sys=0.00, real=1.07 secs]
2015-05-26T16:23:08.447-0200: 65.550: [GC (CMS Final Remark) [YG occupancy: 387920 K (613440 K)]65.550: [Rescan (parallel) , 0.0085125 secs]65.559: [weak refs processing, 0.0000243 secs]65.559: [class unloading, 0.0013120 secs]65.560: [scrub symbol table, 0.0008345 secs]65.561: [scrub string table, 0.0001759 secs][1 CMS-remark: 10812086K(11901376K)] 11200006K(12514816K), 0.0110730 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
2015-05-26T16:23:08.458-0200: 65.561: [CMS-concurrent-sweep-start]
2015-05-26T16:23:08.485-0200: 65.588: [CMS-concurrent-sweep: 0.027/0.027 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
2015-05-26T16:23:08.485-0200: 65.589: [CMS-concurrent-reset-start]
2015-05-26T16:23:08.497-0200: 65.601: [CMS-concurrent-reset: 0.012/0.012 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

Minor GCjvm

從日誌看出,GC事件第一步操做是Minor GC清理Young代。咱們分析一下面示意圖中收集器是如何工做的:性能

圖片描述

  1. GC事件開始的時間。
  2. GC開始相對於JVM啓動時間時間差。
  3. 區分Minor GC和Full GC的標記。它表示是一個Minor GC。
  4. 收集緣由。這個緣由是,一次分配請求不適合在Young代的任何區域。
  5. 使用收集器的名稱。它表示,在Young代中使用的是並行,標記複製,「stop-the-world」算法。被設計在Old代工做的併發標記清除籌集起的組合。
  6. Young代回收先後的已使用空間。
  7. Young代的大小。
  8. 清除時間時長
  9. 回收先後Heap區已使用大小。
  10. Heap大小
  11. Young代垃圾收集器標記複製存活對象消耗時長。包括,CMS收集器通信時長,對象升遷進入Old代的時間,垃圾循環收集結束後的最終清除時間。
  12. GC事件時長-記錄不一樣的類型:
    1.user-回收期間垃圾收集器線程消耗CPU事件
    2.sys-調用操做系統活着等待系統事件消耗時間
    3.real-應用停頓的時鐘時間。Parallel GC時間接近(user+sys)單個垃圾收集器使用線程分開運行時間總和。應當 注意, 由於一些活動不被容許並行執行,它一般超過合計時間。

經過上面咱們能夠看出,回收前heap區已使用空間10,885,349K,Young代已使用空間613,404K。這意味着Old代可用空間是10,271,945K。回收後,Young代減小545,336K可是heap區僅僅減小5,195K。這意味着540,141K是Yong代升遷到Old代的對象的大小。spa

圖片描述

Full GC操作系統

如今,你已經習慣閱讀GC日誌,這章介紹日誌中另一種形式的垃圾收集器。下圖中冗長的輸出囊括了Old代主要垃圾收集器不一樣階段。咱們不一次性地簡單歸納日誌事件,相反,咱們一條一條分析日誌的不一樣階段。回顧一下,CMS收集器相似於下圖:

2015-05-26T16:23:07.321-0200: 64.425: [GC (CMS Initial Mark) [1 CMS-initial-mark: 10812086K(11901376K)] 10887844K(12514816K), 0.0001997 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2015-05-26T16:23:07.321-0200: 64.425: [CMS-concurrent-mark-start]
2015-05-26T16:23:07.357-0200: 64.460: [CMS-concurrent-mark: 0.035/0.035 secs] [Times: user=0.07 sys=0.00, real=0.03 secs]
2015-05-26T16:23:07.357-0200: 64.460: [CMS-concurrent-preclean-start]
2015-05-26T16:23:07.373-0200: 64.476: [CMS-concurrent-preclean: 0.016/0.016 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
2015-05-26T16:23:07.373-0200: 64.476: [CMS-concurrent-abortable-preclean-start]
2015-05-26T16:23:08.446-0200: 65.550: [CMS-concurrent-abortable-preclean: 0.167/1.074 secs] [Times: user=0.20 sys=0.00, real=1.07 secs]
2015-05-26T16:23:08.447-0200: 65.550: [GC (CMS Final Remark) [YG occupancy: 387920 K (613440 K)]65.550: [Rescan (parallel) , 0.0085125 secs]65.559: [weak refs processing, 0.0000243 secs]65.559: [class unloading, 0.0013120 secs]65.560: [scrub symbol table, 0.0008345 secs]65.561: [scrub string table, 0.0001759 secs][1 CMS-remark: 10812086K(11901376K)] 11200006K(12514816K), 0.0110730 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
2015-05-26T16:23:08.458-0200: 65.561: [CMS-concurrent-sweep-start]
2015-05-26T16:23:08.485-0200: 65.588: [CMS-concurrent-sweep: 0.027/0.027 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
2015-05-26T16:23:08.485-0200: 65.589: [CMS-concurrent-reset-start]
2015-05-26T16:23:08.497-0200: 65.601: [CMS-concurrent-reset: 0.012/0.012 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

但要記住,實際上Old代併發收集期間的任什麼時候候,Young代都會發生Minor GC。在這種狀況下,能夠看到上面的Major GC和Minor GC 事件交替執行。

階段1:初始化標記。CMS期間兩次「」stop-the-world「」中的一次在這個階段發生。這個階段的目標是標記Old代全部的GC root可達對象,Young代中被引用的對象。Old代獨立回收這點很重要。

圖片描述

圖片描述

  1. GC時間開始時間,包含時鐘時間和距離相對於JVM啓用的時間,簡單略過貫穿之後的階段的相似的概念。
  2. 回收階段-這時的「」Initial Mark「」收集GC Roots對象。
  3. Old代當前已使用空間。
  4. Old代大小。
  5. heap區大小。
  6. 此次過程持續時間-user,sys,real的實際時間。

階段2: 併發標記。這個階段期間,垃圾回收器掃描整個Old代,標記全部存活對象,從上一個「」Initial Mark「」階段發現的全部Root對象。「」Concurrent Mark「」階段,正如名稱所示,和應用線程併發運行,不會停頓應用線程。因爲標記期間應用改變對象引用,Old代不是全部存活對象也許被標記。

圖片描述

上面的示意圖說明,標記線程併發地移除沒有被「」Current obj「」指針引用的對象。

圖片描述

  1. 收集階段,「」Concurrent Mark「」-掃描整個Old區,標記全部存活對象。
  2. 階段持續時間,展現相對鍾牆時間的消耗時間。
  3. 「」Times「」部分,對於併發階段中計算併發標記開始時間更有意義,不僅是包含併發標記完成工做的時間。

階段3:併發清除 。這是一個併發階段,和應用線程並行執行,不會引發線程停頓。雖然上一個階段和應用線程併發執行,可是一些引用被改變。不管什麼時候發生,JVM標記heap區(「」Card「」)包含突變成「」dirty「」的對象(瞭解 Card Marking)。

圖片描述

在預清理階段,這些「」髒「」對象被佔用,被他們引用的對象依然被標記,當它釋放後,Card被清除。

圖片描述

補充一點,一些必要的最後標記的準備工做被執行。

圖片描述

  1. 收集階段-「Concurrent Preclean」階段-前一個標記階段期間引用發生變化。
  2. 相對於鍾牆時間的消耗時間。
  3. 「」Times「」部分,對於併發階段中計算併發標記開始時間更有意義,不僅是包含併發標記完成工做的時間。

階段4:併發預處理。又一個併發的,不須要「」stop-the-world「」的階段嘗試去儘量多消除最終標記因爲停頓的影響。因爲他作循環作着一樣的事情直到遇到一箇中斷式條件(例如迭代次數,有用工做完成的數量,鍾牆時間消耗,等等),所以這個階段的準確好事取決於這些因素。

圖片描述

  1. 「」併發預清理「」階段。
  2. 整個階段消耗時間,暫時耗費時間和單獨的鐘牆時間。有趣的一點是,user time大大小於時鐘時間。一般咱們看到時鐘時間小於user time,意味着一些工做並行執行,所以時鐘時間小於cpu 時間。如今咱們看一些一些任務:0.167s 的cpu 時間,垃圾收集器線程等待很長時間。本質上,他們儘可能去避開STW階段。默認的,這各階段持續5s。
  3. 「」Times「」部分,對於併發階段中計算併發標記開始時間更有意義,不僅是包含併發標記完成工做的時間。

這個階段會明顯影響到即將帶來的「」stop-the-world「」階段,有不少的配置選項和失敗模式。

階段5:最終標記。這是第二個也是最後一次事件發生期間「」stop-the-world「」的階段。「」stop-the-world「」的目的是最後標記Old代中全部的存活對象。因爲預處理階段是併發的,它們也許趕不上應用線程改變的速度。「」stop-the-world「」須要完成這些考驗。

一般當年輕代爲了儘量多清除幾回緊接着發生的幾回「」stop-the-world「」而CM去執行最終標記。

此次事件看起來比以前的階段複雜得多:

圖片描述

  1. GC 事件開始時間,包括時鐘時間和相對於JVM啓動的時間
  2. 收集階段-「」「「Final Remark」,標記Old代全部的存活對象,包括併發標記階段新建的,應用改變的對象。
  3. 當前已使用的空間和Young代的空間大小
  4. 「掃描」-應用線程停頓期間標記全部的存活對象。這種狀況下併發掃描花費0.0085125 s。
  5. 第一個子階段處理弱引用d的時間戳。
  6. 第二個子階段卸載無用class對象的時間戳。
  7. 最後一個子階段清除擁有class級別元數據的符號和字符串表,和其內部的String。這個階段一般包括時鐘時間。
  8. 這個階段事後Old代已使用和Old代空間大小。
  9. 階段事後heap已使用和heap空間大小。
  10. 階段持續時長。
  11. 階段持續時長,user, system and real 的時間。

最後標記階段事後,Old代全部存活對象被標記,垃圾收集器去準備去回收Old代中的無用對象。

階段6:併發清除。和應用線程併發執行,不須要「」stop-the-world「」。這個階段的目的清除無用對象,以備之後使用。

圖片描述

圖片描述

  1. 清除沒有標記和無用的對象以回收空間。
  2. 展現運行時間和牆鍾時間。
  3. time 部分對併發階段有重大意義,正如併發標記開始時間,包含但不限於併發標記的工做。

階段7: 併發重置。併發執行階段,重置CMS算法內部的數據結構,爲下一個週期作準備。

圖片描述

  1. 併發階段,重置CMS算法內部的數據結構,爲下次循環作準備。
    2.展現運行時間和牆鍾時間。
  2. time 部分對併發階段有重大意義,正如併發標記開始時間,包含但不限於併發標記的工做。

總而言之,CMS收集器經過併發線程卸載大量工做而不用去停頓應用線程在減小階段運行時間上作了偉大的工做。然而,他也存在一些缺點,最值得的注意的是形成Old代碎片化,階段持續時間在某些狀況下缺少可預見性,尤爲在大heap區。

相關文章
相關標籤/搜索