java主要在堆上分配內存,而Java堆又分爲新生代(YoungGen)和老年代(OldGen)兩個部分,新生代又再分爲Eden區和Survivor區兩部分,本文根據java堆的劃分,描述hotspot的內存分配策略。 java
對於採用複製算法的虛擬機,新生代一般有一個Eden區和兩個Survivor區。
對象優先在Eden區分配,當Eden區沒有足夠的空間進行分配時,虛擬機講發起一次Minor GC,Eden中存活的對象將被移動到第一塊Survivor區S1,Eden被清空。
當Eden區再次填滿,再次觸發Minor GC,Eden區和S1中的存活對象被複制送入第二塊Survivor區S2中,S1和Eden被清空,下一輪交換S1和S2的角色。
使用兩個Survivor區可以簡化複製算法的過程,而且避免複製過程當中內存碎片
的產生。 算法
當對象的複製次數達到-XX:MaxTenuringThreshold
設置的值(默認-XX:MaxTenuringThreshold=15)時,將被移至老年代。spa
/** * VM Options: * -Xmx20M 堆最大20M * -Xms20M 堆最小20M * -Xmn10M 新生代爲10M * -XX:SurvivorRatio=8 Eden區和Survivor區的比值爲8:1 * -XX:+PrintGCDetails 輸出回收日誌及退出時內存各區域狀況 */ private static final int _1MB = 1024 * 1024; public static void testAllocation(){ byte[] allocation1,allocation2,allocation3,allocation4; allocation1 = new byte[2 * _1MB]; allocation2 = new byte[2 * _1MB]; allocation3 = new byte[2 * _1MB]; allocation4 = new byte[4 * _1MB]; }
結果:日誌
PSYoungGen total 9216K, used 7479K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000) eden space 8192K, 91% used [0x00000007bf600000,0x00000007bfd4df10,0x00000007bfe00000) from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000) to space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000) ParOldGen total 10240K, used 4096K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000) object space 10240K, 40% used [0x00000007bec00000,0x00000007bf000010,0x00000007bf600000) Metaspace used 2692K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K
在退出時各個區域結果如上, PSYoungGen的PS指的是Parallel Scavenge
垃圾回收器,ParOldGen中的Par指的是Parallel Old
垃圾回收器。
設置中堆爲10M,新生代爲10M,所以老年代爲10M。設置SurvivorRatio爲8:1,所以Eden區大小爲8M,Survivor區大小爲1M。由於Survivor區同一時刻只有一個能用於分配,所以PSYoungGen區域總可用大小爲9M。
在allocation4進行分配時,新生代eden區已經用了6M,只剩2M,沒法進行分配,觸發MinorGC。新生代中3個2M大小的對象所有沒法放入1M的Survivor區中,因此只能經過分配擔保機制將兩個2M的對象放入老年代中,再將allocation4的4M對象放入Eden區中。
最終Eden區分配6M,survivor區中沒有對象,老年代分配4M。code
大對象是指須要大量連續內存空間的Java對象。爲了不在Eden區及兩個Survivor區之間發生大量的內存複製,虛擬機提供了-XX:PretenureSizeThreshold
參數。大於該參數設置值的對象將直接分配在老年代。對象
虛擬機給每一個對象定義了一個對象年齡計數器,對象每通過一個MinorGC仍然存活,則年齡加一,當年齡增長到超過-XX:MaxTenuringThreshold
設置的值(默認爲16)時,將被移至老年代。blog
如文章開頭的例子中,當出現大量對象在MinorGC後仍然存活的狀況,Survivor區沒法容納多餘的對象,此時,須要老年代進行分配擔保,把Survivor沒法容納的對象直接進入老年代。JDk 6 Update 24以後,擔保的規則是,只要老年代的連續空間大於新生代對象總大小或者歷次晉升的平均大小就會進行MinorGC,不然將進行Full GC。內存
參數 | 描述 |
---|---|
-Xms20M | 堆最小值 |
-Xmx20M | 堆最大值 |
-Xmn10M | 新生代大小 |
-XX:SurvivorRatio=8 | Eden區比Survivor區的大小 |
-XX:+PrintGCDetails | 輸出回收日誌和退出時各內存區域狀況 |
-XX:PretenureSizeThreshold=10M | 大於該參數設置值的對象直接分配在老年代 |
-XX:MaxTenuringThreshold=15 | 新生代對象年齡增長到超過該值時,將被移至老年代 |