一、CMS垃圾收集器介紹
CMS是一款優秀的收集器,它的最主要優點在名字上已經體現出來了:併發收集、低停頓,Sun的一些官方文檔裏面也稱之爲併發低停頓收集器(Concurrent Low Pause Collector)。但是CMS還遠達不到完美的程度,它有以下三個顯著的缺點:
- 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收集器無法處理浮動垃圾(Floating Garbage),可能出現“Concurrent Mode Failure”失敗而導致另一次Full GC的產生。由於CMS併發清理階段用戶線程還在運行着,伴隨程序的運行自然還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後,CMS無法在本次收集中處理掉它們,只好留待下一次GC時再將其清理掉。這一部分垃圾就稱爲“浮動垃圾”。也是由於在垃圾收集階段用戶線程還需要運行,即還需要預留足夠的內存空間給用戶線程使用,因此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後,跟着來一次帶壓縮的。
完整配置參數:
package com.tools138.com;
/**
*
* @author alaric
*
*/
public class Test2 {
private static int n = 20;
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
byte[] b1 = getM(50);
byte[] b2 = getM(50);
byte[] b3 = getM(50);
byte[] b4 = getM(50);
byte[] b5 = getM(50);
byte[] b6 = getM(50);
byte[] b7 = getM(5);
byte[] b8 = getM(5);
byte[] b9 = getM(5);
byte[] b10 = getM(5);
byte[] b11 = getM(5);
byte[] b12 = getM(5);
byte[] b13 = getM(5);
byte[] b14 = getM(5);
byte[] b15 = getM(5);
byte[] b16 = getM(5);
byte[] b17 = getM(5);
byte[] b18 = getM(5);
byte[] b19 = getM(5);
byte[] b20 = getM(100);
byte[] b21 = getM(100);
byte[] b22 = getM(100);
byte[] b23 = getM(100);
// Thread.sleep(2000);
}
public static byte[] getM(int m) {
return new byte[1024 * 1024 * m];
}
}
Java HotSpot(TM) 64-Bit Server VM (25.65-b01) for windows-amd64 JRE (1.8.0_65-b17), built on Oct 6 2015 16:39:20 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 4094316k(1410832k free), swap 8186796k(3113544k free)
CommandLine flags: -XX:CMSFullGCsBeforeCompaction=5 -XX:CMSInitiatingOccupancyFraction=85 -XX:CMSMaxAbortablePrecleanTime=5000 -XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/logs/HeapDumpOnOutOfMemoryError.log -XX:InitialHeapSize=536870912 -XX:MaxHeapSize=1048144896 -XX:MaxNewSize=201326592 -XX:MaxTenuringThreshold=6 -XX:NewSize=201326592 -XX:OldPLABSize=16 -XX:ParallelGCThreads=4 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:ThreadStackSize=256 -XX:+UseCMSCompactAtFullCollection -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC
0.279: [GC (Allocation Failure) 0.279: [ParNew: 114985K->553K(176960K), 0.0395416 secs] 114985K->102956K(504640K), 0.0398840 secs] [Times: user=0.17 sys=0.00, real=0.04 secs]
0.353: [GC (Allocation Failure) 0.353: [ParNew: 157128K->715K(176960K), 0.1367165 secs] 259530K->256719K(504640K), 0.1368421 secs] [Times: user=0.47 sys=0.01, real=0.14 secs]
0.498: [GC (CMS Initial Mark) [1 CMS-initial-mark: 256004K(327680K)] 307919K(504640K), 0.0002677 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.498: [CMS-concurrent-mark-start]
0.506: [CMS-concurrent-mark: 0.008/0.008 secs] [Times: user=0.02 sys=0.02, real=0.01 secs]
0.506: [CMS-concurrent-preclean-start]
0.507: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.507: [CMS-concurrent-abortable-preclean-start]
0.507: [GC (Allocation Failure) 0.507: [ParNew: 121133K->16165K(176960K), 0.0562811 secs] 377137K->374569K(540508K), 0.0563903 secs] [Times: user=0.19 sys=0.01, real=0.06 secs]
0.577: [GC (Allocation Failure) 0.577: [ParNew: 118565K->0K(176960K), 0.0739984 secs] 476969K->476703K(658284K), 0.0741047 secs] [Times: user=0.22 sys=0.02, real=0.07 secs]
0.652: [CMS-concurrent-abortable-preclean: 0.001/0.145 secs] [Times: user=0.41 sys=0.03, real=0.14 secs]
0.667: [GC (CMS Final Remark) [YG occupancy: 102400 K (176960 K)]0.667: [Rescan (parallel) , 0.0247611 secs]0.692: [weak refs processing, 0.0000398 secs]0.692: [class unloading, 0.0002673 secs]0.692: [scrub symbol table, 0.0004401 secs]0.693: [scrub string table, 0.0001223 secs][1 CMS-remark: 476703K(481324K)] 579103K(658284K), 0.0258248 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
0.693: [CMS-concurrent-sweep-start]
0.693: [GC (Allocation Failure) 0.693: [ParNew: 102400K->0K(176960K), 0.1390355 secs] 579103K->579103K(760688K), 0.1391037 secs] [Times: user=0.31 sys=0.03, real=0.14 secs]
0.832: [CMS-concurrent-sweep: 0.139/0.139 secs] [Times: user=0.31 sys=0.03, real=0.14 secs]
0.833: [CMS-concurrent-reset-start]
0.836: [CMS-concurrent-reset: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.851: [GC (Allocation Failure) 0.851: [ParNew: 102400K->0K(176960K), 0.1025667 secs] 681504K->681504K(1004352K), 0.1026574 secs] [Times: user=0.14 sys=0.08, real=0.10 secs]
0.968: [GC (CMS Initial Mark) [1 CMS-initial-mark: 681504K(827392K)] 783904K(1004352K), 0.0001363 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.968: [CMS-concurrent-mark-start]
Heap
par new generation total 176960K, used 103973K [0x00000000c1800000, 0x00000000cd800000, 0x00000000cd800000)
eden space 157312K, 66% used [0x00000000c1800000, 0x00000000c7d89600, 0x00000000cb1a0000)
from space 19648K, 0% used [0x00000000cb1a0000, 0x00000000cb1a0000, 0x00000000cc4d0000)
to space 19648K, 0% used [0x00000000cc4d0000, 0x00000000cc4d0000, 0x00000000cd800000)
concurrent mark-sweep generation total 827392K, used 681504K [0x00000000cd800000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 2612K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 287K, capacity 386K, committed 512K, reserved 1048576K