java CMS gc解析

轉載: http://www.blogjava.net/killme2008/archive/2009/09/22/295931.html
    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算法是一致的。

    下面是參數介紹和遇到的問題總結,

一、啓用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.年輕代的並行收集線程數默認是(ncpus <= 8) ? ncpus : 3 + ((ncpus * 5) / 8),若是你但願設定這個線程數,能夠經過-XX:ParallelGCThreads= N 來調整。

8.進入重點,在初步設置了一些參數後,例如:
php

- 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的日誌輸出相似這樣:
html

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的日誌輸出大概是這樣:
java

 [ParNew (promotion failed): 320138K -> 320138K(353920K),  0.2365970  secs] 42576.951 : [CMS: 1139969K -> 1120688K(
2166784K), 
9.2214860  secs] 1458785K -> 1120688K(2520704K),  9.4584090  secs]


這個問題的產生是因爲救助空間不夠,從而向年老代轉移對象,年老代沒有足夠的空間來容納這些對象,致使一次full gc的產生。解決這個問題的辦法有兩種徹底相反的傾向:增大救助空間、增大年老代或者去掉救助空間。 增大救助空間就是調整-XX:SurvivorRatio參數,這個參數是Eden區和Survivor區的大小比值,默認是32,也就是說Eden區是 Survivor區的32倍大小,要注意Survivo是有兩個區的,所以Surivivor其實佔整個young genertation的1/34。調小這個參數將增大survivor區,讓對象儘可能在survitor區呆長一點,減小進入年老代的對象。去掉救助空 間的想法是讓大部分不能立刻回收的數據儘快進入年老代,加快年老代的回收頻率,減小年老代暴漲的可能性,這個是經過將-XX:SurvivorRatio 設置成比較大的值(好比65536)來作到。在咱們的應用中,將young generation設置成256M,這個值相對來講比較大了,而救助空間設置成默認大小(1/34),從壓測狀況來看,沒有出現prommotion failed的現象,年輕代比較大,從GC日誌來看,minor gc的時間也在5-20毫秒內,還能夠接受,所以暫不調整。

Concurrent mode failed的產生是因爲CMS回收年老代的速度太慢,致使年老代在CMS完成前就被沾滿,引發full gc,避免這個現象的產生就是調小-XX:CMSInitiatingOccupancyFraction參數的值,讓CMS更早更頻繁的觸發,下降年老代被沾滿的可能。咱們的應用暫時負載比較低,在生產環境上年老代的增加很是緩慢,所以暫時設置此參數爲80。在壓測環境下,這個參數的表現還能夠,沒有出現過Concurrent mode failed。node

 

莊周夢蝶

生活、程序、將來
   :: 首頁 ::  ::  :: 聚合  :: 管理
 

最新評論

CMS gc實踐總結(糾正併發線程數)

Posted on 2009-09-22 02:10 dennis 閱讀(5488) 評論(1)   編輯   收藏 所屬分類: java
    首先感謝阿寶同窗的幫助,我纔對這個gc算法的調整有了必定的認識,而不是停留在過去僅僅瞭解的階段。在讀過sun的文檔和跟阿寶討論以後,作個小小的總結。
    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算法是一致的。

    下面是參數介紹和遇到的問題總結,

一、啓用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.年輕代的並行收集線程數默認是 (ncpus <= 8) ? ncpus : 3 + ((ncpus * 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(
2166784K), 
9.2214860  secs] 1458785K -> 1120688K(2520704K),  9.4584090  secs]

這個問題的產生是因爲救助空間不夠,從而向年老代轉移對象,年老代沒有足夠的空間來容納這些對象,致使一次full gc的產生。解決這個問題的辦法有兩種徹底相反的傾向: 增大救助空間、增大年老代或者去掉救助空間。 增大救助空間就是調整-XX:SurvivorRatio參數,這個參數是Eden區和Survivor區的大小比值,默認是32,也就是說Eden區是 Survivor區的32倍大小,要注意Survivo是有兩個區的,所以Surivivor其實佔整個young genertation的1/34。調小這個參數將增大survivor區,讓對象儘可能在survitor區呆長一點,減小進入年老代的對象。去掉救助空 間的想法是讓大部分不能立刻回收的數據儘快進入年老代,加快年老代的回收頻率,減小年老代暴漲的可能性,這個是經過將-XX:SurvivorRatio 設置成比較大的值(好比65536)來作到。在咱們的應用中,將young generation設置成256M,這個值相對來講比較大了,而救助空間設置成默認大小(1/34),從壓測狀況來看,沒有出現prommotion failed的現象,年輕代比較大,從GC日誌來看,minor gc的時間也在5-20毫秒內,還能夠接受,所以暫不調整。

Concurrent mode failed的產生是因爲CMS回收年老代的速度太慢,致使年老代在CMS完成前就被沾滿,引發full gc,避免這個現象的產生就是調小 -XX:CMSInitiatingOccupancyFraction參數的值,讓CMS更早更頻繁的觸發,下降年老代被沾滿的可能。咱們的應用暫時負載比較低,在生產環境上年老代的增加很是緩慢,所以暫時設置此參數爲80。在壓測環境下,這個參數的表現還能夠,沒有出現過Concurrent mode failed。


參考資料:
JDK5.0垃圾收集優化之--Don't Pause》 by 江南白衣
《記一次Java GC調整經歷》 1, 2 by Arbow
Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
Tuning Garbage Collection with the 5.0 JavaTM Virtual Machine

  


評論

# re: CMS gc實踐總結  回覆  更多評論   

2009-09-22 11:42 by Scorpio Zhen
不錯,更深刻的學習gc方面的知識
新用戶註冊   刷新評論列表  

 
 
 
     
  找優秀程序員,就在博客園
標題  
姓名  
主頁  
 
驗證碼 *    
內容(請不要發表任何與政治相關的內容)
 
 
相關文章
相關標籤/搜索