瞭解 CMS 垃圾回收日誌

瞭解 CMS 垃圾回收日誌算法

在CMS GC 時,使用參數-XX:+PrintGCDetails 和 -XX:+PrintGCTimeStamps 會輸出不少日誌信息,瞭解這些信息能夠幫咱們更好的調整參數,以得到更高的性能。編程

咱們來看下在JDK1.4.2_10 中CMS GC日誌示例:服務器

39.910: [GC 39.910: [ParNew: 261760K->0K(261952K), 0.2314667 secs] 262017K->26386K(1048384K), 0.2318679 secs]
新生代使用 (ParNew 並行)回收器。新生代容量爲261952K,GC回收後佔用從261760K降到0,耗時0.2314667秒。(譯註:262017K->26386K(1048384K), 0.2318679 secs 表示整個堆佔用從262017K 降至26386K,費時0.2318679)數據結構

40.146: [GC [1 CMS-initial-mark: 26386K(786432K)] 26404K(1048384K), 0.0074495 secs]
開始使用CMS回收器進行老年代回收。初始標記(CMS-initial-mark)階段,這個階段標記由根能夠直接到達的對象,標記期間整個應用線程會暫停。
老年代容量爲786432K,CMS 回收器在空間佔用達到 26386K 時被觸發併發

40.154: [CMS-concurrent-mark-start]
開始併發標記(concurrent-mark-start) 階段,在第一個階段被暫停的線程從新開始運行,由前階段標記過的對象出發,全部可到達的對象都在本階段中標記。oracle

40.683: [CMS-concurrent-mark: 0.521/0.529 secs]
併發標記階段結束,佔用 0.521秒CPU時間, 0.529秒牆鍾時間(也包含線程讓出CPU給其餘線程執行的時間)jvm

40.683: [CMS-concurrent-preclean-start]
開始預清理階段
預清理也是一個併發執行的階段。在本階段,會查找前一階段執行過程當中,重新生代晉升或新分配或被更新的對象。經過併發地從新掃描這些對象,預清理階段能夠減小下一個stop-the-world 從新標記階段的工做量。性能

40.701: [CMS-concurrent-preclean: 0.017/0.018 secs]
預清理階段費時 0.017秒CPU時間,0.018秒牆鍾時間。this

40.704: [GC40.704: [Rescan (parallel) , 0.1790103 secs]40.883: [weak refs processing, 0.0100966 secs] [1 CMS-remark: 26386K(786432K)] 52644K(1048384K), 0.1897792 secs]
Stop-the-world 階段,從根及被其引用對象開始,從新掃描 CMS 堆中殘留的更新過的對象。這裏從新掃描費時0.1790103秒,處理弱引用對象費時0.0100966秒,本階段費時0.1897792 秒。線程

40.894: [CMS-concurrent-sweep-start]
開始併發清理階段,在清理階段,應用線程還在運行。

41.020: [CMS-concurrent-sweep: 0.126/0.126 secs]
併發清理階段費時0.126秒

41.020: [CMS-concurrent-reset-start]
開始併發重置

41.147: [CMS-concurrent-reset: 0.127/0.127 secs]
在本階段,從新初始化CMS內部數據結構,以備下一輪 GC 使用。本階段費時0.127秒

這是CMS正常運行週期打印的日誌,如今讓咱們一塊兒看一下其餘的CMS日誌記錄:

197.976: [GC 197.976: [ParNew: 260872K->260872K(261952K), 0.0000688 secs]197.976: [CMS197.981: [CMS-concurrent-sweep: 0.516/0.531 secs]
(concurrent mode failure): 402978K->248977K(786432K), 2.3728734 secs] 663850K->248977K(1048384K), 2.3733725 secs]

這段信息顯示ParNew 收集器被請求進行新生代的回收,但收集器並無嘗試回收,由於 它 預計在最糟糕的狀況下, CMS 老年代中沒有足夠的空間容納新生代的倖存對象。咱們把這個失敗稱之爲」徹底晉升擔保失敗」。

由於這樣,併發模式的 CMS 被中斷同而且在 197.981秒時,Full GC被啓動。此次Full GC,採用標記-清除-整理算法,會發生stop-the-world,費時2.3733725秒。CMS 老年代佔用從 402978K 降到248977K。

避免併發模式失敗, 經過增長老年代空間大小或者設置參數 CMSInitiatingOccupancyFraction 同時設置UseCMSInitiatingOccupancyOnly爲true。參數 CMSInitiatingOccupancyFraction 的值必須謹慎選擇,設置太低會形成頻繁發生 CMS 回收。

有時咱們發現,當日志中出現晉升失敗時,老年代還有足夠的空間。這是由於」碎片」,老年代中的可用空間並不連續,而重新生代晉升上來的對象,須要一塊連續的可用空間。CMS 收集器是一種非壓縮收集器,在某種類型的應用中會發生碎片。下面博客中 Jon 詳細討論瞭如何處理碎片問題:https://blogs.oracle.com/jonthecollector/entry/when_the_sum_of_the

從JDK 1.5 開始,CMS 收集器中的晉升擔保檢查策略有些變化。原來的策略是考慮最壞狀況,即新生代全部對象都晉升到老年代 , 新的晉升擔保檢查策略基於最近晉升歷史狀況,這種預計晉升對象比最壞狀況下晉升對象要少不少,所以須要的空間也會少點。若是晉升失敗,新生代處於一致狀態。觸發一次 stop-the-world 的標記-壓縮收集. 若是想在 UseSerialGC 中得到這種功能,須要設置參數 -XX:+HandlePromotionFailure.

283.736: [Full GC 283.736: [ParNew: 261599K->261599K(261952K), 0.0000615 secs] 826554K->826554K(1048384K), 0.0003259 secs]
GC locker: Trying a full collection because scavenge failed
283.736: [Full GC 283.736: [ParNew: 261599K->261599K(261952K), 0.0000288 secs]

當一個JNI 關鍵區被釋放時會發生 Stop-the-world GC。新生代由於晉升擔保失敗回收失敗,觸發一次 Full GC.

CMS 能夠運行在增量模式下(i-cms), 使用參數 -XX:+CMSIncrementalMode. 在增量模式下,CMS 收集器在併發階段,不會獨佔整個週期,而會週期性的暫停,喚醒應用線程。收集器把併發階段工做,劃分爲片斷,安排在次級(minor) 回收之間運行。這對須要低延遲,運行在少許CPU服務器上的應用頗有用。

如下是增量模式 CMS的日誌.
2803.125: [GC 2803.125: [ParNew: 408832K->0K(409216K), 0.5371950 secs] 611130K->206985K(1048192K) icms_dc=4 , 0.5373720 secs]
2824.209: [GC 2824.209: [ParNew: 408832K->0K(409216K), 0.6755540 secs] 615806K->211897K(1048192K) icms_dc=4 , 0.6757740 secs]

新生代花費 537 毫秒 和 675 毫秒. 在2次收集之間 iCMS 短暫運行期間由icms_dc 表示,icms_dc 表示運行的佔空比。這裏佔空比爲4% .
簡單計算下, iCMS 增量階段費時 4/100 * (2824.209 – 2803.125 – 0.537) = 821 毫秒, 即 2次 GC 間隔時間的 4% .

在JDK 1.5 中, CMS 增長一個併發可停止預清理(concurrent abortable preclean)階段. 可停止預清理階段,運行在並行預清理和從新標記之間,直到得到所指望的eden空間佔用率。增長這個階段是爲了不在從新標記階段後緊跟着發生一次垃圾清除。爲了儘量區分開垃圾清除和從新標記 ,咱們儘可能安排在兩次垃圾清除之間運行從新標記階段。

There is a second reason why we do this. Immediately following a scavenge there are likely a large number of grey objects that need rescanning. The abortable preclean phase tries to deal with such newly grey objects thus reducing a subsequent CMS remark pause.

能夠經過JVM參數CMSScheduleRemarkEdenSizeThreshold 和 CMSScheduleRemarkEdenPenetration 控制 從新標記階段。默認值是2m和50%. CMSScheduleRemarkEdenSizeThreshold 設置Eden區大小,低於此值時不啓動從新標記階段,由於回報預期爲微不足道 CMSScheduleRemarkEdenPenetration 設置啓動從新標記階段時Eden區的空間佔用率。(譯註:根據下面描述 Eden 應該是指整個新生代)

預清理階段後,若是Eden 空間佔用大於 CMSScheduleRemarkEdenSizeThreshold 設置的值, 會啓動可停止預清理,直到佔用率達到 CMSScheduleRemarkEdenPenetration 設置的值, 不然,咱們當即安排從新標記階段.(譯註:與上面說的正好相反,不知是否是我翻譯有誤)

7688.150: [CMS-concurrent-preclean-start]
7688.186: [CMS-concurrent-preclean: 0.034/0.035 secs]
7688.186: [CMS-concurrent-abortable-preclean-start]
7688.465: [GC 7688.465: [ParNew: 1040940K->1464K(1044544K), 0.0165840 secs] 1343593K->304365K(2093120K), 0.0167509 secs]
7690.093: [CMS-concurrent-abortable-preclean: 1.012/1.907 secs]
7690.095: [GC[YG occupancy: 522484 K (1044544 K)]7690.095: [Rescan (parallel) , 0.3665541 secs]7690.462: [weak refs processing, 0.0003850 secs] [1 CMS-remark: 302901K(1048576K)] 825385K(2093120K), 0.3670690 secs]

上面日誌中,在預清理以後, 啓動可停止預清理, 以後發生年輕代垃圾回收,年輕代佔用從 1040940K 降低到 1464K. 當年輕代佔用率達到 522484K 即堆的50%時,發生從新標記

注意在JDK1.5中,年輕代的垃圾回收日誌輸出在後面的從新標記階段

原創文章,轉載請註明: 轉載自併發編程網 – ifeve.com本文連接地址: 瞭解 CMS 垃圾回收日誌

相關文章
相關標籤/搜索