CMS,全稱Concurrent Low Pause Collector,是jdk1.4後期版本開始引入的新gc算法,在jdk5和jdk6中獲得了進一步改進,它的主要適合場景是對響應時間的重要性需求 大於對吞吐量的要求,可以承受垃圾回收線程和應用線程共享處理器資源,而且應用中存在比較多的長生命週期的對象的應用。CMS是用於對tenured generation的回收,也就是年老代的回收,目標是儘可能減小應用的暫停時間,減小full gc發生的概率,利用和應用程序線程併發的垃圾回收線程來標記清除年老代。在咱們的應用中,由於有緩存的存在,而且對於響應時間也有比較高的要求,所以希 望能嘗試使用CMS來替代默認的server型JVM使用的並行收集器,以便得到更短的垃圾回收的暫停時間,提升程序的響應性。算法
CMS並不是沒有暫停,而是用兩次短暫停來替代串行標記整理算法的長暫停,它的收集週期是這樣:緩存
初始標記(CMS-initial-mark) -> 併發標記(CMS-concurrent-mark) -> 從新標記(CMS-remark) -> 併發清除(CMS-concurrent-sweep) ->併發重設狀態等待下次CMS的觸發(CMS-concurrent-reset)。 其中的1,3兩個步驟須要暫停全部的應用程序線程的。第一次暫停從root對象開始標記存活的對象,這個階段稱爲初始標記;第二次暫停是在併發標記以後, 暫停全部應用程序線程,從新標記併發標記階段遺漏的對象(在併發標記階段結束後對象狀態的更新致使)。第一次暫停會比較短,第二次暫停一般會比較長,而且 remark這個階段能夠並行標記。多線程
而併發標記、併發清除、併發重設階段的所謂併發,是指一個或者多個垃圾回收線程和應用程序線程併發地運行,垃圾回收線程不會暫停應用程序的執行,若是你有多於一個處理器,那麼併發收集線程將與應用線程在不一樣的處理器上運行,顯然,這樣的開銷就是會下降應用的吞吐量。Remark階段的並行,是指暫停了全部應用程序後,啓動必定數目的垃圾回收進程進行並行標記,此時的應用線程是暫停的。併發
CMS的young generation的回收採用的仍然是並行複製收集器,這個跟Paralle gc算法是一致的。性能
該階段進行可達性分析,標記GC ROOT能直接關聯到的對象。 注意是直接關聯間接關聯的對象在下一階段標記。線程
該階段進行GC ROOT TRACING,在第一個階段被暫停的線程從新開始運行。日誌
由前階段標記過的對象出發,全部可到達的對象都在本階段中標記。code
既然類似爲何要有這一步?server
前面咱們講過,CMS是以獲取最短停頓時間爲目的的GC。對象
重標記須要STW(Stop The World),所以重標記的工做盡量多的在併發階段完成來減小STW的時間。
此階段標記重新生代晉升的對象、新分配到老年代的對象以及在併發階段被修改了的對象。
有了前面的基礎,這個階段的工做量被大大減輕,停頓時間所以也會減小。
注意這個階段是多線程的。
一、啓用CMS:-XX:+UseConcMarkSweepGC。 2。CMS默認啓動的回收線程數目是 (ParallelGCThreads + 3)/4) ,若是你須要明確設定,能夠經過-XX:ParallelCMSThreads=20來設定,其中ParallelGCThreads是年輕代的並行收集線程數 三、CMS是不會整理堆碎片的,所以爲了防止堆碎片引發full gc,經過會開啓CMS階段進行合併碎片選項:-XX:+UseCMSCompactAtFullCollection,開啓這個選項必定程度上會影響性能,阿寶的blog裏說也許能夠經過配置適當的CMSFullGCsBeforeCompaction來調整性能,未實踐。 4.爲了減小第二次暫停的時間,開啓並行remark: -XX:+CMSParallelRemarkEnabled。若是remark仍是過長的話,能夠開啓-XX:+CMSScavengeBeforeRemark選項,強制remark以前開始一次minor gc,減小remark的暫停時間,可是在remark以後也將當即開始又一次minor gc。
5.爲了不Perm區滿引發的full gc,建議開啓CMS回收Perm區選項: +CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled
6.默認CMS是在tenured generation沾滿68%的時候開始進行CMS收集,若是你的年老代增加不是那麼快,而且但願下降CMS次數的話,能夠適當調高此值: -XX:CMSInitiatingOccupancyFraction=80
這裏修改爲80%沾滿的時候纔開始CMS回收。
7.年輕代的並行收集線程數默認是(cpu <= 8) ? cpu : 3 + ((cpu * 5) / 8),若是你但願下降這個線程數,能夠經過-XX:ParallelGCThreads= N 來調整。
8.進入重點,在初步設置了一些參數後,例如:
-server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=64m -XX:MaxPermSize=64m -XX:-UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0
須要在生產環境或者壓測環境中測量這些參數下系統的表現,這時候須要打開GC日誌查看具體的信息,所以加上參數:
-verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/home/test/logs/gc.log
在運行至關長一段時間內查看CMS的表現狀況,CMS的日誌輸出相似這樣:
4391.322: [GC [1 CMS-initial-mark: 655374K(1310720K)] 662197K(1546688K), 0.0303050 secs] [Times: user=0.02 sys=0.02, real=0.03 secs] 4391.352: [CMS-concurrent-mark-start] 4391.779: [CMS-concurrent-mark: 0.427/0.427 secs] [Times: user=1.24 sys=0.31, real=0.42 secs] 4391.779: [CMS-concurrent-preclean-start] 4391.821: [CMS-concurrent-preclean: 0.040/0.042 secs] [Times: user=0.13 sys=0.03, real=0.05 secs] 4391.821: [CMS-concurrent-abortable-preclean-start] 4392.511: [CMS-concurrent-abortable-preclean: 0.349/0.690 secs] [Times: user=2.02 sys=0.51, real=0.69 secs] 4392.516: [GC[YG occupancy: 111001 K (235968 K)]4392.516: [Rescan (parallel) , 0.0309960 secs]4392.547: [weak refs processing, 0.0417710 secs] [1 CMS-remark: 655734K(1310720K)] 766736K(1546688K), 0.0932010 secs] [Times: user=0.17 sys=0.00, real=0.09 secs] 4392.609: [CMS-concurrent-sweep-start] 4394.310: [CMS-concurrent-sweep: 1.595/1.701 secs] [Times: user=4.78 sys=1.05, real=1.70 secs] 4394.310: [CMS-concurrent-reset-start] 4394.364: [CMS-concurrent-reset: 0.054/0.054 secs] [Times: user=0.14 sys=0.06, real=0.06 secs]
其中能夠看到CMS-initial-mark階段暫停了0.0303050秒,而CMS-remark階段暫停了0.0932010秒,所以兩次暫停的總共時間是0.123506秒,也就是123毫秒左右。兩次短暫停的時間之和在200如下能夠稱爲正常現象。
可是你極可能遇到兩種fail引發full gc:Prommotion failed和Concurrent mode failed。
Prommotion failed的日誌輸出大概是這樣:
[ParNew (promotion failed): 320138K->320138K(353920K), 0.2365970 secs]42576.951: [CMS: 1139969K->1120688K( 166784K), 9.2214860 secs] 1458785K->1120688K(2520704K), 9.4584090 secs]