GC發生時記憶體分配和回收策略
在《深入理解java虛擬機器》一書中讀到3.6章節,記憶體分配和回收策略:
預備知識
java堆=年輕代(Eden+Survivor+Survivor)+老年代
Eden:Survivor:Survivor預設比例8:1:1,每次年輕代使用率90%(Eden和一個Survivor)。
一 記憶體分配策略
1 物件優先在eden分配
可用空間是Eden和一個Survivor,在GC回收發生時,如果存活的物件比另一個survivor空間要大,就直接放到老年代和survivor中,這部分稱為分配擔保。
2 大物件直接進入老年代
可以通過設定引數,將大物件直接存入老年代,防止了大物件來回複製造成的損失。
3 長期存活的物件將進入老年代
物件在Eden中出生後,並經歷過一次GC後仍存活,且被survivor容納的話,被移動到survivor空間,並記年齡為1歲,物件在每經歷一次MinorGC後,年齡都增加1歲,當年齡到達一定程度就會轉到老年代,用XX:MaxTenuringThreshold引數進行設定。
下面是對第三節一段程式碼的理解。
/* 引數設定為
-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
-XX:MaxTenuringThreshold=1
*/
public class TestAllocation {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
// TODO Auto-generated method stub
testTenuringThreshold();
}
public static void testTenuringThreshold(){
byte[] all1,all2,all3;
all1 = new byte[ _1MB/4];
//什麼時候進入老年代取決於XX:MaxTenuringThreshold設定
all2 = new byte[4* _1MB];
all3 = new byte[4* _1MB];
all3 = null;
//all3 = new byte[4* _1MB];
}
}
上述程式碼執行結果
如果把註釋去掉
//all3 = new byte[4* _1MB];
結果是如下
二 結果分析
那麼為什麼會這樣,下面分析記憶體變化
圖1 是執行如下兩行後結果
all1 = new byte[ _1MB/4];
all2 = new byte[4* _1MB];
圖2
all3 = new byte[4* _1MB];
要給all3分配記憶體,沒空間,進行第一次GC,再在Eden分配空間給all3
圖3
all3 = null;
all3 = new byte[4* _1MB];
又要分配新記憶體4M,沒空間,所以進行第二次GC,原來的4M空間的all3引用不再指向它,按理這次GC被回收,圖3是進行GC後的圖,Eden區因為沒有引用指向它被回收,而Survivor2變數因達到優年輕代轉向老年代閾值,而被複制到老年代。
可以從GC日誌中看出,第二次GC後
[GC[DefNew: 4905K->0K(9216K), 0.0006405 secs],年輕代成為空。
圖4
是給all3分配記憶體的圖。所以第二個程式最終結果是圖3,而第一個程式最終結果是圖2。