在閱讀《深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版) (華章原創(chuàng)精品) - 周志明》一書中,為增強理解冈止,跟著實踐有感笤昨。
書中第3.8.1節(jié):對象優(yōu)先在Eden分配龟再,實驗結果如下:
然后我得到的不一樣的結果(開始質疑書本的正確性)呀洲,后來想到可能是因為JDK版本不同,使用的垃圾收集器不同啡捶。所以后面特意指定了垃圾收集器-XX:+UseSerialGC(虛擬機運行在Client模式下的默認值乍楚,Serial+Serial Old)就與書上結果一致了。
為避免其他因素干擾届慈,我在測試前使用了System.gz()強制JVM執(zhí)行一次full gc徒溪。
private static final int _1MB = 1024 *1024;
/**
* VM參數(shù):-XX:+UseSerialGC -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
* 注意不同的收集器內存分配和回收策略不一樣,本例使用-XX:+UseSerialGC臊泌,虛擬機運行在Client模式下的默認值渠概,Serial+Serial Old播揪。得到的結果和書上一致
*/
public static void testAllocation() {
System.gc();
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]; // 出現(xiàn)一次Minor GC
}
下面展示每一步的內存分配和回收情況猪狈,可看到初始直接執(zhí)行full gc后也并不能完全回收年輕代和老年代雇庙,會有一小部分空間被占用疆前。然后allocation1 在eden區(qū)成功分配了2MB竹椒,allocation2胸完,allocation3也成功分配舶吗,至此eden區(qū)已有6MB择膝。
根據(jù)我們給JVM設置的參數(shù)肴捉,堆內存20MB,不可擴展(初始內存和最大內存都為20MB)傲隶,年輕代10MB(可得老年代也為10MB)窃页,eden區(qū)和survivor區(qū)的比例為8(即8:1:1脖卖,survivor分from和to)畦木,所以eden區(qū)有8MB蛆封,survivor區(qū)1M惨篱,年輕代可用空間為9MB(年輕代采用的標記-復制算法妒蛇,eden+survivor中的存活對象復制到另一個survivor)绣夺。
年輕代還剩不到2MB欢揖,不足以分配allocation4她混,因此觸發(fā)了GC(原因是內存分配失敗)馒过,而目前的6MB對象都還存活,無法回收腹忽,只好通過分配擔保機制提前轉移到老年代,所以老年代變成了6MB砚作,然后年輕代分配了allocation4,變?yōu)?MB葫录。
/**
System.gc();
// 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]; // 出現(xiàn)一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->724K(10240K), 0.0024904 secs] 2162K->724K(19456K), [Metaspace: 3426K->3426K(1056768K)], 0.0025292 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 246K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 3% used [0x00000000fec00000, 0x00000000fec3d890, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 724K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 7% used [0x00000000ff600000, 0x00000000ff6b5240, 0x00000000ff6b5400, 0x0000000100000000)
Metaspace used 3434K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 374K, capacity 388K, committed 512K, reserved 1048576K
/**
System.gc();
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]; // 出現(xiàn)一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->713K(10240K), 0.0021630 secs] 1994K->713K(19456K), [Metaspace: 3295K->3295K(1056768K)], 0.0022069 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 2458K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 30% used [0x00000000fec00000, 0x00000000fee66858, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 713K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 6% used [0x00000000ff600000, 0x00000000ff6b2600, 0x00000000ff6b2600, 0x0000000100000000)
Metaspace used 3318K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 362K, capacity 388K, committed 512K, reserved 1048576K
/**
System.gc();
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]; // 出現(xiàn)一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->710K(10240K), 0.0018954 secs] 1994K->710K(19456K), [Metaspace: 3221K->3221K(1056768K)], 0.0019325 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4506K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 55% used [0x00000000fec00000, 0x00000000ff066808, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 710K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 6% used [0x00000000ff600000, 0x00000000ff6b1920, 0x00000000ff6b1a00, 0x0000000100000000)
Metaspace used 3363K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 369K, capacity 388K, committed 512K, reserved 1048576K
/**
System.gc();
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]; // 出現(xiàn)一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->710K(10240K), 0.0020691 secs] 1994K->710K(19456K), [Metaspace: 3192K->3192K(1056768K)], 0.0021076 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 6554K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 80% used [0x00000000fec00000, 0x00000000ff266818, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 710K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 6% used [0x00000000ff600000, 0x00000000ff6b1828, 0x00000000ff6b1a00, 0x0000000100000000)
Metaspace used 3205K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 351K, capacity 388K, committed 512K, reserved 1048576K
/**
System.gc();
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]; // 出現(xiàn)一次Minor GC
*/
[Full GC (System.gc()) [Tenured: 0K->724K(10240K), 0.0027738 secs] 2162K->724K(19456K), [Metaspace: 3421K->3421K(1056768K)], 0.0028359 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 6471K->25K(9216K), 0.0022628 secs] 7196K->6893K(19456K), 0.0022878 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4203K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
from space 1024K, 2% used [0x00000000ff500000, 0x00000000ff5064f8, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 6868K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 67% used [0x00000000ff600000, 0x00000000ffcb5138, 0x00000000ffcb5200, 0x0000000100000000)
Metaspace used 3447K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 376K, capacity 388K, committed 512K, reserved 1048576K
下面再來驗證下JDK8默認的垃圾收集器米同,首先CMD命令查看使用的是哪種收集器
java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=267464320 -XX:MaxHeapSize=4279429120 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
UseParallelGC (虛擬機運行在Server模式下的默認值面粮,Parallel Scavenge+Serial Old(PS Mark Sweep),再查看詳細信息
java -XX:+PrintGCDetails -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
Heap
PSYoungGen total 76288K, used 2621K [0x000000076af80000, 0x0000000770480000, 0x00000007c0000000)
eden space 65536K, 4% used [0x000000076af80000,0x000000076b20f748,0x000000076ef80000)
from space 10752K, 0% used [0x000000076fa00000,0x000000076fa00000,0x0000000770480000)
to space 10752K, 0% used [0x000000076ef80000,0x000000076ef80000,0x000000076fa00000)
ParOldGen total 175104K, used 0K [0x00000006c0e00000, 0x00000006cb900000, 0x000000076af80000)
object space 175104K, 0% used [0x00000006c0e00000,0x00000006c0e00000,0x00000006cb900000)
Metaspace used 2351K, capacity 4480K, committed 4480K, reserved 1056768K
class space used 254K, capacity 384K, committed 384K, reserved 1048576K
我們將對應參數(shù)改為-XX:+UseParallelGC韭山,再來看一下結果:
allocation1, allocation2钱磅,allocation3可直接分配盖淡,與上文一致,不同的是allocation4凿歼,直接被分到了老年代(用的應該是大對象直接進入老年代的分配規(guī)則)褪迟,由此可知不同收集器的實現(xiàn)方式是有差異的。
/**
System.gc();
// 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]; // 出現(xiàn)一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 1994K->776K(9216K)] 1994K->784K(19456K), 0.0006403 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 776K->0K(9216K)] [ParOldGen: 8K->705K(10240K)] 784K->705K(19456K), [Metaspace: 3174K->3174K(1056768K)], 0.0031807 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 9216K, used 246K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 3% used [0x00000000ff600000,0x00000000ff63d890,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 705K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb0560,0x00000000ff600000)
Metaspace used 3185K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 350K, capacity 388K, committed 512K, reserved 1048576K
/**
System.gc();
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]; // 出現(xiàn)一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 2162K->808K(9216K)] 2162K->816K(19456K), 0.0015479 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 808K->0K(9216K)] [ParOldGen: 8K->716K(10240K)] 816K->716K(19456K), [Metaspace: 3339K->3339K(1056768K)], 0.0030067 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 9216K, used 2457K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 30% used [0x00000000ff600000,0x00000000ff8667f8,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 716K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 7% used [0x00000000fec00000,0x00000000fecb33a8,0x00000000ff600000)
Metaspace used 3362K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 369K, capacity 388K, committed 512K, reserved 1048576K
/**
System.gc();
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]; // 出現(xiàn)一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 1994K->840K(9216K)] 1994K->848K(19456K), 0.0007088 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 840K->0K(9216K)] [ParOldGen: 8K->711K(10240K)] 848K->711K(19456K), [Metaspace: 3257K->3257K(1056768K)], 0.0030776 secs] [Times: user=0.06 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 9216K, used 4506K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 55% used [0x00000000ff600000,0x00000000ffa66808,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 711K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb1ce0,0x00000000ff600000)
Metaspace used 3337K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 363K, capacity 388K, committed 512K, reserved 1048576K
/**
System.gc();
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]; // 出現(xiàn)一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 1994K->840K(9216K)] 1994K->848K(19456K), 0.0006964 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 840K->0K(9216K)] [ParOldGen: 8K->710K(10240K)] 848K->710K(19456K), [Metaspace: 3245K->3245K(1056768K)], 0.0028236 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 9216K, used 6554K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 80% used [0x00000000ff600000,0x00000000ffc66878,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 710K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb1ac8,0x00000000ff600000)
Metaspace used 3302K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 361K, capacity 388K, committed 512K, reserved 1048576K
/**
System.gc();
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]; // 出現(xiàn)一次Minor GC
*/
[GC (System.gc()) [PSYoungGen: 1994K->792K(9216K)] 1994K->800K(19456K), 0.0013828 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 792K->0K(9216K)] [ParOldGen: 8K->711K(10240K)] 800K->711K(19456K), [Metaspace: 3259K->3259K(1056768K)], 0.0031560 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 9216K, used 6570K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 80% used [0x00000000ff600000,0x00000000ffc6a828,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 4807K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 46% used [0x00000000fec00000,0x00000000ff0b1d60,0x00000000ff600000)
Metaspace used 3447K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 376K, capacity 388K, committed 512K, reserved 1048576K
補充:
設置垃圾收集器參數(shù)
-XX:+UseSerialGC答憔,虛擬機運行在Client模式下的默認值味赃,Serial+Serial Old。
-XX:+UseParNewGC虐拓,ParNew+Serial Old心俗,在JDK1.8被廢棄,在JDK1.7還可以使用。
-XX:+UseConcMarkSweepGC城榛,ParNew+CMS+Serial Old揪利。
-XX:+UseParallelGC,虛擬機運行在Server模式下的默認值狠持,Parallel Scavenge+Serial Old(PS Mark Sweep)疟位。
-XX:+UseParallelOldGC,Parallel Scavenge+Parallel Old喘垂。
-XX:+UseG1GC甜刻,G1+G1。