JVM中新创建的对象在Eden区分配内存,Eden区空间不足时,触发minorGC回收内存,对于一些大对象则直接分配在老年代,那么这里所谓的大对象到底是多大呢?本文将用测试结果回答这个问题!
验证目标
本文通过简单的例子,不断调大对象的大小来观察该对象在堆的哪一部分内存区域分配、相关的大对象边界值,以及对GC的影响。
测试案例
测试例子代码如下:
1package messageSenderTest;
2
3public class JVMTest1 {
4 // java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCCause -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
5 private static final int MB = 1024 * 1024; // 1M
6 private static final int KB = 1024; // 1 kb
7 public static void main(String[] args) {
8 System.out.println("before mark1");
9 byte[] arr1 = new byte[4 * MB];
10 System.out.println("before mark2");
11 // 1.分配1MB 2.分配2MB 3.分配3071KB 4.分配3MB 5.分配4M 6.分配5M 7.分配6M 8.分配8M 9.分配10M
12 byte[] arr2 = new byte[1024 * KB];
13 System.out.println("after mark2");
14 System.out.println("refSize:" + arr1.length + arr2.length);
15 }
16}
JVM参数:
1java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
其中,
- -Xms20m:堆的初始大小20m(memory size)
- -Xmx20m:堆的最大大小(memory max size)
- -Xmn10m:堆的新生代大小(memory new)
- -verbose:gc:打印GC日志,标准参数
- -XX:+PrintGCDetails:打印GC日志,非标准参数
- -XX:+PrintHeapAtGC:打印GC前后堆的信息
- -XX:SurvivorRatio=3:新生代中Eden Space和From Space比例为3:1
也是就说,堆的总大小20M,新生代10M,老年代10M,其中,Eden区6M,S0和S1各2M。上面代码中先后创建2个字节数组arr1和arr2,arr1大小4M保持不变,下面的例子围绕着修改对象arr2的大小来展开。
基础内存消耗
注释main方法的所有内容,执行程序,发现堆空间自身占用了大约1MB左右的内存。
1$ java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
2Heap
3 PSYoungGen total 8192K, used 1048K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
4 eden space 6144K, 17% used [0x00000000ff600000,0x00000000ff706180,0x00000000ffc00000)
5 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
6 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
7 ParOldGen total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
8 object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
9 Metaspace used 2575K, capacity 4486K, committed 4864K, reserved 1056768K
10 class space used 282K, capacity 386K, committed 512K, reserved 1048576K
PSYoungGen total 8192K
表示新生代的大小是8M,为啥不是10M?Eden区大小6M,而S0和S1的大小分别2M,但是二者总有一个是空的,所以加起来8M!
分配1M大小的对象
假设字节数组arr1大小4MB,字节数组arr2大小1MB。代码如下:
1 public static void main(String[] args) {
2 System.out.println("before mark1");
3 byte[] arr1 = new byte[4 * MB];
4 System.out.println("before mark2");
5 // 1.分配1MB
6 byte[] arr2 = new byte[1024 * KB];
7 System.out.println("after mark2");
8 System.out.println("refSize:" + arr1.length + " " + arr2.length);
9 }
运行的结果为:
1$ java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
2before mark1
3before mark2
4after mark2
5refSize:4194304 1048576
6Heap
7 PSYoungGen total 8192K, used 6144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
8 eden space 6144K, 100% used [0x00000000ff600000,0x00000000ffc00000,0x00000000ffc00000)
9 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
10 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
11 ParOldGen total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
12 object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
13 Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
14 class space used 282K, capacity 386K, committed 512K, reserved 1048576K
字节数组arr1大小4M,自身1M左右,分配字节数组arr2的大小也是1M,没有执行GC说明Eden区刚好容纳得下。如果将arr2的大小改为2M为怎么样?
分配2M大小的对象
如果让字节数组arr1的大小不变,字节数组arr2的大小修改为2048KB,代码如下:
1public static void main(String[] args) {
2 System.out.println("before mark1");
3 byte[] arr1 = new byte[4 * MB];
4 System.out.println("before mark2");
5 //2.分配2MB
6 byte[] arr2 = new byte[2048 * KB];
7 System.out.println("after mark2");
8 System.out.println("refSize:" + arr1.length + " " + arr2.length);
9 }
执行结果为:
1$ java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
2before mark1
3before mark2
4{Heap before GC invocations=1 (full 0):
5 PSYoungGen total 8192K, used 5021K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
6 eden space 6144K, 81% used [0x00000000ff600000,0x00000000ffae7498,0x00000000ffc00000)
7 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
8 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
9 ParOldGen total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
10 object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
11 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
12 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
13[GC (Allocation Failure) [PSYoungGen: 5021K->680K(8192K)] 5021K->4784K(18432K), 0.0027871 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
14Heap after GC invocations=1 (full 0):
15 PSYoungGen total 8192K, used 680K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
16 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
17 from space 2048K, 33% used [0x00000000ffc00000,0x00000000ffcaa020,0x00000000ffe00000)
18 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
19 ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
20 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002010,0x00000000ff600000)
21 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
22 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
23}
24after mark2
25refSize:4194304 2097152
26Heap
27 PSYoungGen total 8192K, used 2909K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
28 eden space 6144K, 36% used [0x00000000ff600000,0x00000000ff82d6c8,0x00000000ffc00000)
29 from space 2048K, 33% used [0x00000000ffc00000,0x00000000ffcaa020,0x00000000ffe00000)
30 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
31 ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
32 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002010,0x00000000ff600000)
33 Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
34 class space used 282K, capacity 386K, committed 512K, reserved 1048576K
以上说明,在分配大小为2M的字节数组arr2时触发了minorGC,执行前新生代大小5021K,全部集中在Eden区,这刚好是字节数组arr1的大小加上基础消耗内存的1M。此时老年代是空的,目前新生代空间富余。执行GC后新生代的大小变为680K,全部集中在from space区,老年代的大小是4104K。这说明本次minorGC时,数组arr1的4M对象直接从新生代晋升到老年代,同时Eden区中基础消耗对象有680K进入了幸存区,其余部分被GC回收掉了。
执行GC后Eden区是空的,分配2M大小给arr2对象,此时Eden区的使用率为36%,如上面日志的最下面部分所示:eden space 6144K, 36% used
。
分配3M大小的对象
如果将字节数组arr2的大小修改为3M会怎样?
1 public static void main(String[] args) {
2 System.out.println("before mark1");
3 byte[] arr1 = new byte[4 * MB];
4 System.out.println("before mark2");
5 // 1.分配1MB 2.分配2MB 3.分配3071KB 4.分配3MB 5.分配4M 6.分配5M 7.分配6M 8.分配8M 9.分配10M
6 byte[] arr2 = new byte[3072 * KB];
7 System.out.println("after mark2");
8 System.out.println("refSize:" + arr1.length + " " + arr2.length);
9 }
运行的结果为:
1$ java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
2before mark1
3before mark2
4after mark2
5refSize:4194304 3145728
6Heap
7 PSYoungGen total 8192K, used 5144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
8 eden space 6144K, 83% used [0x00000000ff600000,0x00000000ffb06190,0x00000000ffc00000)
9 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
10 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
11 ParOldGen total 10240K, used 3072K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
12 object space 10240K, 30% used [0x00000000fec00000,0x00000000fef00010,0x00000000ff600000)
13 Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
14 class space used 282K, capacity 386K, committed 512K, reserved 1048576K
居然没有执行GC的相关日志,什么情况?分析上面的日志,新生代使用了5144K,全部在Eden区,断定为字节数组arr1的4M内存加上基础消耗内存。老年代使用了3072K,这不刚好就是字节数组对象arr2的大小嘛!说明JVM为数组arr2对象分配内存时,Eden区内存不足,本该触发minorGC的,但是大小为3M数组arr2为大对象,所以直接晋升到老年代分配内存,不涉及GC。那么问题来了,多大的对象被认定大对象呢直接晋升到老年代呢?于是我将arr2的大小修改为3071K,
1 public static void main(String[] args) {
2 System.out.println("before mark1");
3 byte[] arr1 = new byte[4 * MB];
4 System.out.println("before mark2");
5 // 1.分配1MB 2.分配2MB 3.分配3071KB 4.分配3MB 5.分配4M 6.分配5M 7.分配6M 8.分配8M 9.分配10M
6 byte[] arr2 = new byte[3071 * KB];
7 System.out.println("after mark2");
8 System.out.println("refSize:" + arr1.length + " " + arr2.length);
9 }
执行的结果为:
1$ java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
2before mark1
3before mark2
4{Heap before GC invocations=1 (full 0):
5 PSYoungGen total 8192K, used 5021K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
6 eden space 6144K, 81% used [0x00000000ff600000,0x00000000ffae7498,0x00000000ffc00000)
7 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
8 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
9 ParOldGen total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
10 object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
11 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
12 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
13[GC (Allocation Failure) [PSYoungGen: 5021K->680K(8192K)] 5021K->4784K(18432K), 0.0025586 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
14Heap after GC invocations=1 (full 0):
15 PSYoungGen total 8192K, used 680K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
16 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
17 from space 2048K, 33% used [0x00000000ffc00000,0x00000000ffcaa020,0x00000000ffe00000)
18 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
19 ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
20 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002010,0x00000000ff600000)
21 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
22 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
23}
24after mark2
25refSize:4194304 3144704
26Heap
27 PSYoungGen total 8192K, used 3932K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
28 eden space 6144K, 52% used [0x00000000ff600000,0x00000000ff92d2c8,0x00000000ffc00000)
29 from space 2048K, 33% used [0x00000000ffc00000,0x00000000ffcaa020,0x00000000ffe00000)
30 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
31 ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
32 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002010,0x00000000ff600000)
33 Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
34 class space used 282K, capacity 386K, committed 512K, reserved 1048576K
不看具日志体内容也能猜出此时触发了minorGC,这说明对象的大小3071K没有被认定为大对象直接在老年代分配内存,而是尝试GC,只相差1K结果就完全不一样,而3M刚好是Eden区大小的一般,于是我断定分配的对象内存大于或等于Eden区空间的1/2时,被JVM认定为大对象进而直接在老年代分配内存,同时不触发minorGC。
分配4M大小的对象
接着上面的结论,既然3M为大对象,4M的对象更是了,应该不会触发GC把,于是将第二个字节数组对象arr2的大小修改为4M,然后观察结果。
1 private static final int MB = 1024 * 1024; // 1M
2 private static final int KB = 1024; // 1 kb
3 public static void main(String[] args) {
4 System.out.println("before mark1");
5 byte[] arr1 = new byte[4 * MB];
6 System.out.println("before mark2");
7 // 1.分配1MB 2.分配2MB 3.分配3071KB 4.分配3MB 5.分配4M 6.分配5M 7.分配6M 8.分配8M 9.分配10M
8 byte[] arr2 = new byte[4096 * KB];
9 System.out.println("after mark2");
10 System.out.println("refSize:" + arr1.length + " " + arr2.length);
11 }
运行结果和预期一样,数组arr2作为大对象直接在老年代分配内存,没有GC。
1$ java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
2before mark1
3before mark2
4after mark2
5refSize:4194304 4194304
6Heap
7 PSYoungGen total 8192K, used 5144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
8 eden space 6144K, 83% used [0x00000000ff600000,0x00000000ffb06190,0x00000000ffc00000)
9 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
10 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
11 ParOldGen total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
12 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
13 Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
14 class space used 282K, capacity 386K, committed 512K, reserved 1048576K
分配5M,6M,7M,8M,9M大小的对象
分配大于4M的对象,测试结果和4M一样,都是直接分配到老年代而已。 代码和运行结果如下:
1 public static void main(String[] args) {
2 System.out.println("before mark1");
3 byte[] arr1 = new byte[4 * MB];
4 System.out.println("before mark2");
5 // 1.分配1MB 2.分配2MB 3.分配3071KB 4.分配3MB 5.分配4M 6.分配5M 7.分配6M 8.分配8M 9.分配10M
6 byte[] arr2 = new byte[8000 * KB];
7 System.out.println("after mark2");
8 System.out.println("refSize:" + arr1.length + " " + arr2.length);
9 }
运行结果:
1$ java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
2before mark1
3before mark2
4after mark2
5refSize:4194304 8192000
6Heap
7 PSYoungGen total 8192K, used 5144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
8 eden space 6144K, 83% used [0x00000000ff600000,0x00000000ffb06190,0x00000000ffc00000)
9 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
10 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
11 ParOldGen total 10240K, used 8000K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
12 object space 10240K, 78% used [0x00000000fec00000,0x00000000ff3d0010,0x00000000ff600000)
13 Metaspace used 2577K, capacity 4486K, committed 4864K, reserved 1056768K
14 class space used 282K, capacity 386K, committed 512K, reserved 1048576K
分配10M大小的对象
老年代的大小是10M,本人尝试分配10239K的对象,程序正常,不会发生OOM,当刚好分配10M的对象时,发生OOM。测试代码为:
1 public static void main(String[] args) {
2 System.out.println("before mark1");
3 byte[] arr1 = new byte[4 * MB];
4 System.out.println("before mark2");
5 // 1.分配1MB 2.分配2MB 3.分配3071KB 4.分配3MB 5.分配4M 6.分配5M 7.分配6M 8.分配8M 9.分配10M
6 byte[] arr2 = new byte[10240 * KB];
7 System.out.println("after mark2");
8 System.out.println("refSize:" + arr1.length + " " + arr2.length);
9 }
运行的输出结果如下,日志好长,说明进行了多次GC:
1$ java -Xms20m -Xmx20m -Xmn10m -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:SurvivorRatio=3 test.JVMTest
2before mark1
3before mark2
4{Heap before GC invocations=1 (full 0):
5 PSYoungGen total 8192K, used 5021K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
6 eden space 6144K, 81% used [0x00000000ff600000,0x00000000ffae7498,0x00000000ffc00000)
7 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
8 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
9 ParOldGen total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
10 object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
11 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
12 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
13[GC (Allocation Failure) [PSYoungGen: 5021K->648K(8192K)] 5021K->4752K(18432K), 0.0028166 secs] [Times: user=0.05 sys=0.02, real=0.00 secs]
14Heap after GC invocations=1 (full 0):
15 PSYoungGen total 8192K, used 648K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
16 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
17 from space 2048K, 31% used [0x00000000ffc00000,0x00000000ffca2020,0x00000000ffe00000)
18 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
19 ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
20 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002010,0x00000000ff600000)
21 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
22 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
23}
24{Heap before GC invocations=2 (full 0):
25 PSYoungGen total 8192K, used 648K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
26 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
27 from space 2048K, 31% used [0x00000000ffc00000,0x00000000ffca2020,0x00000000ffe00000)
28 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
29 ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
30 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002010,0x00000000ff600000)
31 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
32 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
33[GC (Allocation Failure) [PSYoungGen: 648K->640K(8192K)] 4752K->4744K(18432K), 0.0007924 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
34Heap after GC invocations=2 (full 0):
35 PSYoungGen total 8192K, used 640K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
36 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
37 from space 2048K, 31% used [0x00000000ffe00000,0x00000000ffea0030,0x0000000100000000)
38 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
39 ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
40 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002010,0x00000000ff600000)
41 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
42 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
43}
44{Heap before GC invocations=3 (full 1):
45 PSYoungGen total 8192K, used 640K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
46 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
47 from space 2048K, 31% used [0x00000000ffe00000,0x00000000ffea0030,0x0000000100000000)
48 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
49 ParOldGen total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
50 object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002010,0x00000000ff600000)
51 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
52 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
53[Full GC (Allocation Failure) [PSYoungGen: 640K->0K(8192K)] [ParOldGen: 4104K->4615K(10240K)] 4744K->4615K(18432K), [Metaspace: 2571K->2571K(1056768K)], 0.0048151 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
54Heap after GC invocations=3 (full 1):
55 PSYoungGen total 8192K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
56 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
57 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
58 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
59 ParOldGen total 10240K, used 4615K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
60 object space 10240K, 45% used [0x00000000fec00000,0x00000000ff081ef0,0x00000000ff600000)
61 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
62 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
63}
64{Heap before GC invocations=4 (full 1):
65 PSYoungGen total 8192K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
66 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
67 from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
68 to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
69 ParOldGen total 10240K, used 4615K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
70 object space 10240K, 45% used [0x00000000fec00000,0x00000000ff081ef0,0x00000000ff600000)
71 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
72 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
73[GC (Allocation Failure) [PSYoungGen: 0K->0K(8192K)] 4615K->4615K(18432K), 0.0002409 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
74Heap after GC invocations=4 (full 1):
75 PSYoungGen total 8192K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
76 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
77 from space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
78 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
79 ParOldGen total 10240K, used 4615K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
80 object space 10240K, 45% used [0x00000000fec00000,0x00000000ff081ef0,0x00000000ff600000)
81 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
82 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
83}
84{Heap before GC invocations=5 (full 2):
85 PSYoungGen total 8192K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
86 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
87 from space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
88 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
89 ParOldGen total 10240K, used 4615K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
90 object space 10240K, 45% used [0x00000000fec00000,0x00000000ff081ef0,0x00000000ff600000)
91 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
92 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
93[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(8192K)] [ParOldGen: 4615K->4603K(10240K)] 4615K->4603K(18432K), [Metaspace: 2571K->2571K(1056768K)], 0.0050322 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
94Heap after GC invocations=5 (full 2):
95 PSYoungGen total 8192K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
96 eden space 6144K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffc00000)
97 from space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
98 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
99 ParOldGen total 10240K, used 4603K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
100 object space 10240K, 44% used [0x00000000fec00000,0x00000000ff07ef70,0x00000000ff600000)
101 Metaspace used 2571K, capacity 4486K, committed 4864K, reserved 1056768K
102 class space used 281K, capacity 386K, committed 512K, reserved 1048576K
103}
104Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
105 at test.JVMTest.main(JVMTest.java:12)
106Heap
107 PSYoungGen total 8192K, used 181K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
108 eden space 6144K, 2% used [0x00000000ff600000,0x00000000ff62d6b8,0x00000000ffc00000)
109 from space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000)
110 to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
111 ParOldGen total 10240K, used 4603K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
112 object space 10240K, 44% used [0x00000000fec00000,0x00000000ff07ef70,0x00000000ff600000)
113 Metaspace used 2601K, capacity 4486K, committed 4864K, reserved 1056768K
114 class space used 285K, capacity 386K, committed 512K, reserved 1048576K
从日志上可以看到,当分配了10M这样一个和老年代空间大小一样大的对象时,发生内存溢出,先进行了2次minorG,然后进行了3次FullGC,接着就报发生OOM了。有趣的是,这个OOM发生时其实只是老年代的空间满了,新生代只使用了2%而已。
本文结论
根据上面的测试验证,可以看出,一般情况下,JVM在Eden区给新创建的对象分配内存,当Eden区空间不足时触发minorG腾出空间,但是当该对象的大小大于或等于Eden区空间的一般时,将直接在老年代分配内存,不涉及minorGC,如果老年代也容纳不下了就会发生OOM异常。