[譯]Java8官方GC調優指南 --(八)CMS收集器

本套文章是Java8官方GC調優指南的全文翻譯,點擊查看原文,原文章名稱《Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide》html

8 Concurrent Mark Sweep (CMS) Collector CMS收集器

使用參數-XX:+UseConcMarkSweepGC來啓用CMS收集器。java

相似於其餘的收集器,CMS收集器也是分代的;minor 和 major都有。CMS收集器嘗試經過使用獨立的垃圾收集器線程,在應用程序線程執行的同時跟蹤可到達的對象,從而減小因爲major gc而致使的暫停時間。在每一個major gc週期中,CMS收集器在收集開始時暫停全部應用程序線程一小段時間,而後在收集中期再次暫停。第二次停頓每每是兩次停頓中較長的停頓。在這兩個暫停期間,使用多個線程來執行收集工做。收集的其他部分(包括對活動對象的大部分跟蹤和對不可到達對象的清掃)是使用一個或多個收集器線程完成的,此時應用程序自身的線程也在併發執行。minor gc能夠與正在進行的major gc循環交叉進行,並以相似於parallel collector的方式進行(特別是,應用程序線程在minor gc期間也會停頓)。算法

Concurrent Mode Failure 併發模式失敗

CMS收集器使用一個或多個垃圾收集器線程,這些線程與應用程序線程同時運行,其目標是在永久代滿以前完成對其的收集。如前所述,在正常操做中,CMS收集器在應用程序線程仍然運行的狀況下執行大部分跟蹤和清除工做,所以應用程序線程只能看到短暫的暫停。然而,若是CMS收集器在tenured區填滿以前回收全部不可達對象,或者tenured區的剩餘空閒空間已經不足以分配一個新的對象,那麼就會產生停頓——全部應用的線程所有中止,直到完成一次Full GC。沒辦法完成併發收集被稱爲Concurrent Mode Failure,這代表須要調整CMS收集器參數。若是併發收集被顯式垃圾收集(System.gc())或爲提供診斷工具提供所需信息,則會報告併發模式中斷。安全

Excessive GC Time and OutOfMemoryError 過長的GC時間和OOM

和平行收集器差很少,很少說bash

Floating Garbage 漂浮垃圾

CMS收集器與Java HotSpot VM中的全部其餘收集器同樣,是一種跟蹤收集器,它至少標識堆中全部可達對象。用Richard Jones和Rafael D. Lins在他們的書《垃圾收集:自動動態內存算法》中的說法,它是一個增量更新收集器。因爲應用程序線程和垃圾收集器線程在major gc期間併發運行,垃圾收集器線程跟蹤的對象可能在收集過程結束時就變爲不可達的了。這種還沒有回收的不可達對象稱爲Floating Garbage。Floating Garbage的數量取決於併發收集週期的持續時間和應用程序引用更新(也稱爲突變)的頻率。此外,因爲young區和tenured區是獨立收集的,一個是另外一個的root。一個粗暴的方法是,考慮Floating Garbage對內存的消耗,能夠嘗試將tenured區的大小增長20%,防止Floating Garbage形成OOM。併發

一個併發收集週期結束時堆中新產生的Floating Garbage將在下一個收集週期中被清理掉。oracle

Pauses 停頓

CMS收集器在併發收集週期中會形成兩次停頓ide

第一次停頓是將從GC root(例如,來自線程堆棧和寄存器、靜態對象等的對象引用)和堆中的其餘區域(例如,young區)能直接訪問到的對象標記爲活動的。工具

第一次停頓稱爲initial mark pause 初始化標記停頓。ui

第二次停頓出如今併發跟蹤階段的結束以後,尋找那些併發跟蹤階段沒跟蹤到的對象,這種對象通常是因爲程序線程引用恰好在併發跟蹤後發生了變化。

第二次停頓稱爲remark pause 再標記停頓。

Concurrent Phases 併發停頓

對可達對象的併發追蹤階段發生在initial mark pause和remark pause之間。在這個併發跟蹤階段,一個或多個併發垃圾收集器線程可能正在使用處理器資源(CPU),不然應用程序就可使用這些資源。所以,即便應用程序線程沒有暫停,在這個階段和其餘併發階段,應用程序的吞吐量依然可能會降低(由於CMS要消耗CPU時間片)。在remark pause以後,併發清除階段CMS將收集到的對象標記爲不可達。收集週期完成後,CMS收集器將等待,幾乎不消耗任何計算資源,直到下一個major gc開始。

Starting a Concurrent Collection Cycle 啓動一次併發收集週期

對於串行收集器,只要tenured區已滿,就會觸發major gc,而且在收集完成以前中止全部應用程序線程。相反,併發收集都是定時開始的,因此收集能夠在tenured區滿以前結束;不然,應用程序因爲Concurrent Mode Failure觸發Full GC產生更長的暫停。有幾種方法能夠啓動併發收集。

根據最近的歷史記錄,CMS收集器維護了一個預估的tenured耗盡的剩餘時間和併發收集週期所需的時間。基於這些動態的估算,就會啓動一次併發收集週期,目的是在tenured區耗盡以前完成此次收集循環。這些預估是爲了安全而進行的,由於併發模式失敗的代價可能很是大。

若是tenured區的佔用超過初始佔用(一個tenured區的百分比),也會啓動併發收集。該初始佔用閾值的默認值約爲92%,可是該值可能隨版本的不一樣而變化。這個值可使用參數調整: -XX:CMSInitiatingOccupancyFraction=<N>,其中是tenured區大小的整數百分比(0到100)。

Scheduling Pauses 按期停頓

young區收集和tenured區收集的停頓是獨立的。它們不重疊,但能夠快速地連續發生,一次收集的停頓,緊接着另外一次收集的暫停,看起來像是一次更長的停頓。爲了不這種狀況,CMS收集器嘗試將remark pause安排在兩次young區停頓之間的中間。這種調度目前還不支持 initial mark pause,它一般比remark pause短得多。

Incremental Mode 增量模式

請注意,Incremental Mode在Java SE 8中被棄用,可能在未來的主要版本中被刪除。

CMS收集器能夠在以遞增的方式完成併發階段。回想一下,在併發階段,垃圾收集器線程使用一個或多個處理器。Incremental Mode的目的是經過週期性地yield併發階段線程從而將處理器交還給應用程序,來減小長併發階段的影響。這種模式在這裏稱爲i-cms,它將收集器併發完成的工做劃分爲小塊時間,而且分佈在屢次young gc之間。當應用程序部署在CPU核數低的機器上(小於等於雙核),而且須要低停頓時間時,此特性會比較有用。

併發收集週期一般包括如下步驟

  • 中止全部的應用線程,從gc root識別可達對象,而後恢復全部應用線程。
  • 併發追蹤可達對象圖,使用1或多個處理器,同時應用線程也在正常運行。
  • 併發從新追蹤那些上一階段以後被修改的對象,使用1個處理器
  • 中止全部線程,從新追蹤gc root和對象圖,防止上一階段以後對象引用又出現變動,而後恢復全部應用線程。
  • 併發清除不可達對象,使用單處理器。
  • 併發resize整個heap,爲下次收集循環準備好數據,使用單處理器。

一般,CMS收集器在整個併發跟蹤階段使用一個或多個處理器,而不會主動放棄它們。相似地,在整個併發清除階段使用一個處理器,一樣不放棄它。這些開銷對於一個有響應時間限制的應用來講,可能太大了,尤爲是那種運行在單核或者雙核的物理機服務。Incremental Mode經過將併發階段拆解成分佈在minor gc之間的多個部分來解決這個問題。(經過Thread.yield方法交還CPU讓應用程序線程得到執行機會)

i-cms模式在CMS放棄處理器資源以前使用duty cycle來控制併發階段的工做量。duty cycle是兩次minor gc的之間容許CMS回收器工做的時間百分比。i-cms模式能夠根據應用程序的行爲(推薦的方法,稱爲自動步調)自動計算duty cycle,duty cycle也能夠經過參數來設置。

Command-Line Options 命令行選項

下表是ims的命令行參數:

Option Description Java5以前的默認值 Java6以後的默認值
-XX:+CMSIncrementalMode 開啓增量模式,同時也會開啓CMS 禁用 禁用
-XX:CMSIncrementalPacing 開啓自動步長,duty cycle會自動調整 禁用 禁用
-XX:CMSIncrementalDutyCycle= minor gc之間cms的執行時間百分比,若是設置了pacing,那麼這個參數就是初始值 50 10
-XX:CMSIncrementalDutyCycleMin= 開啓pacing後duty cycle的下界 10 0
-XX:CMSIncrementalSafetyFactor= 計算duty cycle時穩定增長的百分比 10 10
-XX:CMSIncrementalOffset= duty cycle右移的百分比 0 0
-XX:CMSExpAvgFactor= 對當前樣本數量加權的百分比 25 25

Recommended Options

Java 8 要使用i-cms,使用下面的命令行參數:

-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps
複製代碼

後面兩個參數就是打日誌用的,能夠後期分析GC行爲。

Basic Troubleshooting 基本的問題定位

i-cms自動步長特性在程序運行期間收集各項指標來計算duty cycle,這樣併發收集能夠在heap被填滿以前完成。可是,經過過去的行爲也不能一直預測外來的行爲,這個預估可能也不會那麼精確。若是發生了過多的GC,能夠根據下表來進行調優:

Step Options
增長安全係數 -XX:CMSIncrementalSafetyFactor=
增長最小duty cycle -XX:CMSIncrementalDutyCycleMin=
關閉自動步長,使用固定的duty cycle -XX:-CMSIncrementalPacing -XX:CMSIncrementalDutyCycle=

Measurements 度量

下面的日誌片斷是CMS收集器增長參數-verbose:gc-XX:+PrintGCDetails後輸出的。

注意,CMS收集器的輸出與minor gc的輸出穿插在一塊兒;一般,許多minor gc發生在併發收集週期中。CMS-initial-mark表示併發收集週期的開始,CMS-concurrent-mark表示併發標記階段的結束,CMS-concurrent-sweep表示併發清除階段的結束。以前沒有討論過CMS-concurrent-preclean表明的的預清理階段。preclean階段和remark階段是併發進行的。最後一個階段是CMS-concurrent-reset,表示正在爲下一次併發收集作準備。

[GC [1 CMS-initial-mark: 13991K(20288K)] 14103K(22400K), 0.0023781 secs]
[GC [DefNew: 2112K->64K(2112K), 0.0837052 secs] 16103K->15476K(22400K), 0.0838519 secs]
...
[GC [DefNew: 2077K->63K(2112K), 0.0126205 secs] 17552K->15855K(22400K), 0.0127482 secs]
[CMS-concurrent-mark: 0.267/0.374 secs]
[GC [DefNew: 2111K->64K(2112K), 0.0190851 secs] 17903K->16154K(22400K), 0.0191903 secs]
[CMS-concurrent-preclean: 0.044/0.064 secs]
[GC [1 CMS-remark: 16090K(20288K)] 17242K(22400K), 0.0210460 secs]
[GC [DefNew: 2112K->63K(2112K), 0.0716116 secs] 18177K->17382K(22400K), 0.0718204 secs]
[GC [DefNew: 2111K->63K(2112K), 0.0830392 secs] 19363K->18757K(22400K), 0.0832943 secs]
...
[GC [DefNew: 2111K->0K(2112K), 0.0035190 secs] 17527K->15479K(22400K), 0.0036052 secs]
[CMS-concurrent-sweep: 0.291/0.662 secs]
[GC [DefNew: 2048K->0K(2112K), 0.0013347 secs] 17527K->15479K(27912K), 0.0014231 secs]
[CMS-concurrent-reset: 0.016/0.016 secs]
[GC [DefNew: 2048K->1K(2112K), 0.0013936 secs] 17527K->15479K(27912K), 0.0014814 secs]
複製代碼

相對於minor gc的停頓時間,initial mark停頓時間一般較短。併發階段(併發標記、併發預清理和併發清理)一般持續的時間明顯長於minor gc的停頓時間,如上面的日誌所示。可是,請注意,應用程序在這些併發階段不會停頓,雖然持續時間長,可是注意程序不會停頓。remark形成的停頓時間一般與minor gc停頓時間至關。remark停頓受某些應用程序特徵(例如,高頻率修改對象引用可能會增長此停頓的持續時間)和上一次minor gc的持續時間(例如,young區的對象越多,停頓時間越長)的影響。

相關文章
相關標籤/搜索