JAVA垃圾收集器之CMS收集器,垃圾收集cms
1、特點
CMS收集器是JAVA虛擬機器中垃圾收集器的一種。它運行在JAVA虛擬機器的老年代中。CMS是(Concurrent MarkSweep)的首字母縮寫。CMS收集器是一種以擷取最短回收停頓時間為目標的收集器。比較適用於互連網等場合,可能是互連網中最重要的收集器模式;
2、優點
由於整個過程中耗時最長的並發標記和並發清除過程中,收集器線程都可以與使用者線程一起工作,所以總體上來說,CMS收集器的記憶體回收過程是與使用者線程一起並發地執行的。因此CMS是一款優秀的收集器,具備了並發收集、低停頓的優點,Sun的一些官方文檔裡面也稱之為並發低停頓收集器(Concurrent Low Pause Collector)。
3、缺點
CMS收集器對CPU資源非常敏感
面向並發設計的程式都對CPU資源比較敏感。在並發階段,它雖然不會導致使用者線程停頓,但是會因為佔用了一部分線程(或者說CPU資源)而導致應用程式變慢,總輸送量會降低。CMS預設啟動的回收線程數是(CPU數量+3)/ 4,也就是當CPU在4個以上時,並發回收時垃圾收集線程最多佔用不超過25%的CPU資源。但是當CPU不足4個時(譬如2個),那麼CMS對使用者程式的影響就可能變得很大,如果CPU負載本來就比較大的時候,還分出一半的運算能力去執行收集器線程,就可能導致使用者程式的執行速度忽然降低了50%,這也很讓人受不了。為瞭解決這種情況,虛擬機器提供了一種稱為“增量式並發收集器”(Incremental Concurrent Mark Sweep / i-CMS)的CMS收集器變種,所做的事情和單CPU年代PC機作業系統使用搶佔式來類比多任務機制的思想一樣,就是在並發標記和並發清理的時候讓GC線程、使用者線程交替運行,盡量減少GC線程的獨佔資源的時間,這樣整個垃圾收集的過程會更長,但對使用者程式的影響就會顯得少一些,速度下降也就沒有那麼明顯,但是目前版本中,i-CMS已經被聲明為“deprecated”,即不再提倡使用者使用。
CMS收集器無法處理浮動垃圾
由於CMS並發清理階段使用者線程還在運行著,伴隨程式的運行自然還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後,CMS無法在本次收集中處理掉它們,只好留待下一次GC時再將其清理掉。這一部分垃圾就稱為“浮動垃圾(Floating Garbage)”。也是由於在垃圾收集階段使用者線程還需要運行,即還需要預留足夠的記憶體空間給使用者線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,需要預留一部分空間提供並發收集時的程式運作使用。在預設設定下,CMS收集器在老年代使用了68%的空間後就會被啟用,這是一個偏保守的設定,如果在應用中老年代增長不是太快,可以適當調高參數-XX:CMSInitiatingOccupancyFraction的值來提高觸發百分比,以便降低記憶體回收次數以擷取更好的效能。要是CMS運行期間預留的記憶體無法滿足程式需要,就會出現一次“Concurrent Mode Failure”失敗,這時候虛擬機器將啟動後備預案:臨時啟用Serial Old收集器來重新進行老年代的垃圾收集,這樣停頓時間就很長了。所以說參數-XX:CMSInitiatingOccupancyFraction設定得太高將會很容易導致大量“Concurrent Mode Failure”失敗,效能反而降低。
收集結束時會產生大量空間片段
CMS是一款基於“標記-清除”演算法實現的收集器,在收集結束時會產生大量空間片段。空間片段過多時,將會給大對象分配帶來很大的麻煩,往往會出現老年代還有很大的空間剩餘,但是無法找到足夠大的連續空間來分配當前對象,不得不提前觸發一次Full GC。為瞭解決這個問題,CMS收集器提供了一個-XX:+UseCMSCompactAtFullCollection切換參數,用於在“享受”完Full GC服務之後額外免費附送一個磁碟重組過程,記憶體整理的過程是無法並發的。空間片段問題沒有了,但停頓時間不得不變長了。虛擬機器設計者們還提供了另外一個參數-XX: CMSFullGCsBeforeCompaction,這個參數用於設定在執行多少次不壓縮的Full GC後,跟著來一次帶壓縮的。
4、代碼
package com.gc;
import java.util.ArrayList;
import java.util.List;
/**
* 簡單的JAVA虛擬機器記憶體回收,cms收集器的使用
* 參數:-Xms30m -Xmx60m-Xmn10m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails
* @author 範芳銘
*/
public class EasyCMS {
public byte[] placeHolder =new byte[64 * 1024]; //預留位置
public static voidmain(String[] args) throws Exception{
outOfMemoryByExpansionSize();
}
private static voidoutOfMemoryByExpansionSize() throws Exception{
List<EasyCMS>list = new ArrayList<EasyCMS>();
while(true){
EasyCMS serial =new EasyCMS();
list.add(serial);
Thread.sleep(10);//停頓10毫秒
}
}
}
5、參數
參數:-Xms30m -Xmx60m-Xmn10m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails
-Xms30m JVM最小30M
-Xmx100m JVM最大100M
-Xmn10m 新生代固定10M
-XX:+ UseConcMarkSweepGC對應parNew + cms收集器
-XX:+PrintGCDetails 列印詳細GC
6、運行結果
…
[GC [ParNew: 9110K->972K(9216K),0.0056278 secs] 48708K->48702K(60416K), 0.0056664 secs] [Times: user=0.00sys=0.00, real=0.01 secs]
[GC [1 CMS-initial-mark:47730K(51200K)] 48766K(60416K), 0.0000829 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]
[CMS-concurrent-mark: 0.003/0.003secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-preclean:0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC[YG occupancy: 1036 K (9216K)][Rescan (parallel) , 0.0001092 secs][weak refs processing, 0.0000140 secs][1 CMS-remark: 47730K(51200K)] 48766K(60416K), 0.0001609 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-sweep: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-reset: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [ParNew: 9112K->9112K(9216K),0.0000185 secs][CMS: 47728K->51175K(51200K), 0.0184446 secs]56840K->56814K(60416K), [CMS Perm : 2086K->2085K(12288K)], 0.0185255secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
[GC [1 CMS-initial-mark:51175K(51200K)] 56878K(60416K), 0.0000866 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]
[CMS-concurrent-mark: 0.003/0.003secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-preclean:0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-abortable-preclean:0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC[YG occupancy: 5703 K (9216K)][Rescan (parallel) , 0.0001108 secs][weak refs processing, 0.0000041 secs][1 CMS-remark: 51175K(51200K)] 56878K(60416K), 0.0001523 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-sweep: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-reset: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [ParNew: 9103K->9103K(9216K),0.0000140 secs][CMS: 51172K->51172K(51200K), 0.0135952 secs]60275K->60269K(60416K), [CMS Perm : 2085K->2084K(12288K)], 0.0136662secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC [CMS:51172K->51172K(51200K), 0.0041339 secs] 60269K->60269K(60416K), [CMS Perm: 2084K->2084K(12288K)], 0.0041729 secs] [Times: user=0.01 sys=0.00,real=0.01 secs]
[GC[1 CMS-initial-mark: 51172K(51200K)] 60269K(60416K), 0.0000883 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]
…
7、CMS收集器的運作步驟
CMS收集器是基於“標記-清除”演算法實現的,它的運作過程相對於前面幾種收集器來說要更複雜一些,整個過程分為6個步驟,包括:
初始標記(CMS initial mark)
並發標記(CMS concurrent mark)
並發預清理(CMS-concurrent-preclean)
重新標記(CMS remark)
並發清除(CMS concurrent sweep)
並發重設(CMS-concurrent-reset)
其中初始標記、重新標記這兩個步驟仍然需要“Stop The World”。初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,並發標記階段就是進行GC Roots Tracing的過程,而重新標記階段則是為了修正並發標記期間,因使用者程式繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。其他動作都是並發的。
對應具體的運行結果:
[GC [ParNew: 9110K->972K(9216K),0.0056278 secs] 48708K->48702K(60416K), 0.0056664 secs] [Times: user=0.00sys=0.00, real=0.01 secs]
【1.初始標記】
[GC [1 CMS-initial-mark:47730K(51200K)] 48766K(60416K), 0.0000829 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]
【2.並發標記】
[CMS-concurrent-mark: 0.003/0.003secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
【3.並發預清理】
[CMS-concurrent-preclean:0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
【4.重新標記】
[GC[YG occupancy: 1036 K (9216K)][Rescan (parallel) , 0.0001092 secs][weak refs processing, 0.0000140 secs][1 CMS-remark: 47730K(51200K)] 48766K(60416K), 0.0001609 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]
【5.並發清除】
[CMS-concurrent-sweep: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
【6.並發重設】
[CMS-concurrent-reset: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]