1. 程式人生 > >GC發生時記憶體分配和回收策略

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]; } }

上述程式碼執行結果
1次GC
如果把註釋去掉

//all3 = new byte[4* _1MB];

結果是如下
2次GC

二 結果分析
那麼為什麼會這樣,下面分析記憶體變化
圖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。

記憶體分析