在JVM虛擬機中有一個參數是啓動後打印GC日誌的,能夠打印出在運行時,JVM運行Java程序的內存中的GC信息。閱讀GC日誌是處理Java虛擬機內存問題的基礎技能,每一個收集器的日誌格式均可以不同。但虛擬機設計者爲了方便用戶閱讀,將各個收集器的日誌都維持必定的共性。bash
咱們能夠用過在程序啓動時設置該參數,來打印信息。多線程
-XX:+PrintGCDetails
複製代碼
下面將按照兩個經典的GC日誌,來學習如何查看GC日誌:學習
33.125:[GC[DefNew:3324K->152K(3712K),0.0025925 secs],3324K-152K(11904K),0.0031680 secs] 100.667:[F u l l G C[Tenured:0K->210 K(10240K), 0.0149142 secs]4603K->210K(19456K),[Perm:2999K->2999K(21248K)],0.0150007 secs][Times:user=0.01 sys=0.00,real=0.02 secs]spa
第一列,最前面的數字:「33.125:」和「100.667:」表明了GC發生的時間,這個數字的含義是從Java虛擬機啓動以來通過的秒數。線程
第二列:GC日誌開頭的「[GC」和「[Full GC」說明了此次垃圾收集的停頓類型,而不是用來區分新生代GC仍是老年代GC的。若是有「Full」,說明此次GC是發生了Stop-The-World的.若是是調用System.gc() 方法所觸發的收集,那麼在這裏將顯示「[Full GC(System)」。設計
第三列:接下來的「[DefNew」、「[Tenured」、「[Perm」表示GC發生的區域,這裏顯示的區域名稱與使用的GC收集器是密切相關的,例如上面樣例所使用的Serial收集器中的新生代名爲「DefaultNew Generation」,因此顯示的是「[DefNew」。若是是ParNew收集器,新生代名稱就會變爲「[ParNew」,意爲「Parallel New Generation」。若是採用Parallel Scavenge收集器,那它配套的新生代稱爲「PSYoungGen」,老年代和永久代同理,名稱也是由收集器決定的。調試
後面方括號內部的「3324K->152K(3712K)」含義是「GC前該內存區域已使用容量->GC後該內存區域已使用容量(該內存區域總容量)」。而在方括號以外的「3324K->152K(11904K)」表示「GC前Java堆已使用容量->GC後Java堆已使用容量(Java堆總容量)」。日誌
再日後,「0.0025925 secs」表示該內存區域GC所佔用的時間,單位是秒。有的收集器會給出更具體的時間數據,如「[Times:user=0.01 sys=0.00,real=0.02 secs]」,這裏面的user、sys和real與Linux的time命令所輸出的時間含義一致,分別表明用戶態消耗的CPU時間、內核態消耗的CPU事件和操做從開始到結束所通過的牆鍾時間(Wall Clock Time)。CPU時間與牆鍾時間的區別是,牆鍾時間包括各類非運算的等待耗時,例如等待磁盤I/O、等待線程阻塞,而CPU時間不包括這些耗時,但當系統有多CPU或者多核的話,多線程操做會疊加這些CPU時間,因此讀者看到user或sys時間超過real時間是徹底正常的。code
我還有一個我本地的例子:cdn
代碼:
public class TestPretenureSizeThreshold {
private static final int E_1MB=1024*1024;
/**
*VM參數:-verbose:gc-Xms20M-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:SurvivorRatio=8
*-XX:PretenureSizeThreshold=3145728
*/
public static void testPretenureSizeThreshold(){
byte[]allocation;
//直接分配在老年代中
allocation=new byte[4*E_1MB];
}
public static void main(String[] args) {
TestPretenureSizeThreshold.testPretenureSizeThreshold();
// System.gc();
}
}
複製代碼
最後分享垃圾收集器的一些經常使用參數總結,大家能夠試着,去調試不一樣的收集器看打印的格式有什麼區別?