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异常。