CMS垃圾收集器

CMS全稱 ConcurrentMarkSweep,是一款併發的、使用標記-清除算法的垃圾回收器, 若是老年代使用CMS垃圾回收器,須要添加虛擬機參數-「XX:+UseConcMarkSweepGC」
缺點:java

  1. CMS收集器對CPU資源很是敏感,在併發階段,它雖然不會致使用戶線程停頓,可是因爲佔用了一部分線程,因此會致使應用程序變慢,總吞吐量下降。CMS默認啓動的回收線程數是(cpu數量+3)/4。
  2. CMS收集器沒法處理浮動垃圾,可能出現「Concurrent Mode Failure」失敗而致使一次Full GC。在JDK1.6中,CMS收集器當老年代使用了92%的空間後纔會進行收集,因此若是CMS運行期間預留的內存沒法知足程序須要,就會出現一次「Concurrent Mode Failure」。經過-XX:CMSInitiatingOccupancyFraction能夠調整百分比。
    該參數必須配合UseCMSInitiatingOccupancyOnly使用纔有效
  3. CMS因爲是基於標記-清除算法實現的收集器,因此就可能會在收集結束的時候產生大量空間碎片。若是老年代中沒有足夠大的連續空間來分配當前對象,那麼就會可能提早觸發Full GC並進行碎片整理
    JVM參數
-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps 
-XX:+PrintGCDateStamps 
-Xloggc:D:\aa.gc 
-XX:+PrintTenuringDistribution 
-XX:+UseConcMarkSweepGC 
-Xmx1024m 
-Xms1024m

日誌以下:算法

2019-04-11T16:30:48.164+0800: 14.035: [GC (CMS Initial Mark) [1 CMS-initial-mark: 0K(699072K)] 33843K(1013632K), 0.0066881 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] 
2019-04-11T16:30:48.179+0800: 14.044: [CMS-concurrent-mark-start] 
2019-04-11T16:30:48.179+0800: 14.044: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2019-04-11T16:30:48.179+0800: 14.044: [CMS-concurrent-preclean-start] 
2019-04-11T16:30:48.179+0800: 14.047: [CMS-concurrent-preclean: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2019-04-11T16:30:48.179+0800: 14.047: [CMS-concurrent-abortable-preclean-start] 
2019-04-11T16:30:52.428+0800: 18.302: [CMS-concurrent-abortable-preclean: 2.860/4.254 secs] [Times: user=9.47 sys=1.09, real=4.25 secs] 
2019-04-11T16:30:52.428+0800: 18.302: [GC (CMS Final Remark) [YG occupancy: 203965 K (314560 K)]2019-04-11T16:30:52.428+0800: 18.303: [Rescan (parallel) , 0.0177423 secs]2019-04-11T16:30:52.447+0800: 18.320: [weak refs processing, 0.0001500 secs]2019-04-11T16:30:52.447+0800: 18.320: [class unloading, 0.0287879 secs]2019-04-11T16:30:52.475+0800: 18.349: [scrub symbol table, 0.0121125 secs]2019-04-11T16:30:52.497+0800: 18.362: [scrub string table, 0.0012159 secs][1 CMS-remark: 0K(699072K)] 203965K(1013632K), 0.0623200 secs] [Times: user=0.13 sys=0.00, real=0.07 secs]

Old GC過程併發

  1. 初始化標記(CMS Initial Mark)
    2019-04-11T16:30:48.164+0800: 14.035: [GC (CMS Initial Mark) [1 CMS-initial-mark: 0K(699072K)] 33843K(1013632K), 0.0066881 secs] [Times: user=0.09 sys=0.00, real=0.01 secs]
    整個過程會STW。
    標記GC Roots可達的老年代對象;
    遍歷新生代對象,標記可達的老年代對象;
  2. 併發標記(CMS-concurrent-mark)
2019-04-11T16:30:48.179+0800: 14.044: [CMS-concurrent-mark-start] 
2019-04-11T16:30:48.179+0800: 14.044: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

該階段GC線程和應用線程併發執行,遍歷InitialMarking階段標記出來的存活對象,而後繼續遞歸標記這些對象可達的對象。jvm

  1. 預處理(CMS-concurrent-preclean)
2019-04-11T16:30:48.179+0800: 14.044: [CMS-concurrent-preclean-start] 
2019-04-11T16:30:48.179+0800: 14.047: [CMS-concurrent-preclean: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

處理新生代已經發現的引用,好比在併發階段,在Eden區中分配了一個A對象,A對象引用了一個老年代對象B(這個B以前沒有被標記),在這個階段就會標記對象B爲活躍對象。學習

  1. 可中斷的預清理(CMS-concurrent-abortable)
2019-04-11T16:30:48.179+0800: 14.047: [CMS-concurrent-abortable-preclean-start] 
2019-04-11T16:30:52.428+0800: 18.302: [CMS-concurrent-abortable-preclean: 2.860/4.254 secs] [Times: user=9.47 sys=1.09, real=4.25 secs]

該階段發生的前提是,新生代Eden區的內存使用量大於參數 CMSScheduleRemarkEdenSizeThreshold 默認是2M,若是新生代的對象太少,就沒有必要執行該階段,直接執行從新標記階段。
由於CMS GC的終極目標是下降垃圾回收時的暫停時間,因此在該階段要盡最大的努力去處理那些在併發階段被應用線程更新的老年代對象,這樣在暫停的從新標記階段就能夠少處理一些,暫停時間也會相應的下降。線程

  1. 從新標記(CMS Final Remark)
2019-04-11T16:30:52.428+0800: 18.302: [GC (CMS Final Remark) [YG occupancy: 203965 K (314560 K)]2019-04-11T16:30:52.428+0800: 18.303: [Rescan (parallel) , 0.0177423 secs]2019-04-11T16:30:52.447+0800: 18.320: [weak refs processing, 0.0001500 secs]2019-04-11T16:30:52.447+0800: 18.320: [class unloading, 0.0287879 secs]2019-04-11T16:30:52.475+0800: 18.349: [scrub symbol table, 0.0121125 secs]2019-04-11T16:30:52.497+0800: 18.362: [scrub string table, 0.0012159 secs][1 CMS-remark: 0K(699072K)] 203965K(1013632K), 0.0623200 secs] [Times: user=0.13 sys=0.00, real=0.07 secs]

併發從新標記,STW過程。
遍歷新生代對象,從新標記根據GC Roots,從新標記。日誌

針對併發模式失效的調優

發生併發模式失效每每是因爲CMS不能以足夠快的速度清理老年代空間:新生代須要進行垃圾回收時,CMS收集器計算髮現老年代沒有足夠的空閒空間能夠容納這些晉級對象,不得不先對老年代進行垃圾回收。
當老年代空間的佔用達到某個程度時,併發回收就開始了。一個CMS後臺線程要在老年代剩餘空間用盡以前,完成老年代空間的掃描回收工做。若是併發回收速度太慢,就會發生併發模式失效。
能夠經過如下途徑避免這種失效:code

  1. 想辦法增大老年代空間,要麼只移動部分的新生代對象到老年代,要麼增長更多的堆空間。
  2. 以更高的頻率運行後臺回收線程。
  3. 使用更多的後臺回收線程。
    若是有更多的內存可用,更好的方案是增長堆的大小,不然能夠嘗試調整後臺線程運行的方式來解決這個問題。

給後臺線程更多的運行機會

同時設置-XX:CMSInitiatingOccupancyFraction=N和-XX:+UseCMSInitiatingOccupancyOnly。對特定的程序,該標誌的更優值能夠根據GC日誌中CMS週期首次啓動失敗時的值獲得。具體方法是,在垃圾回收日誌中尋找併發模式失效,找到後再反向查找CMS週期最近的啓動記錄。日誌中含有CMS-initial-mark信息的一行包含了CMS週期啓動時,老年代空間的佔用狀況。對象

調整CMS後臺線程

每一個CMS後臺線程都會100%地佔用機器上的一顆CPU。若是應用程序發生併發模式失效,同時又有額外的CPU週期可用,能夠設置-XX:ConcGCThreads=N標誌,增長後臺線程的數目。默認ConcGCThreads =(ParallelGCThreads+3)/4。當有4個並行線程時,有1個併發線程,
當有5~8個並行線程時,有2個併發線程。ParallelGCThreads表示的是GC並行時使用的線程數,若是新生代使用ParNew,那麼ParallelGCThreads也就是新生代GC線程數。默認狀況下,當CPU數量小於8時,ParallelGCThreads的值就是CPU的數量,當CPU數量大於8時,ParallelGCThreads的值等於3+5*cpuCount/8。遞歸

java學習筆記/jvm

相關文章
相關標籤/搜索