对象进入老年代的四种形式
  • 大对象
  • 动静年龄判断
  • minor gc后,survivor区空间不能包容全副存活对象
  • 存活对象达到年龄阈值。比方15

这一节次要讲:minor gc后,survivor区空间不能包容全副存活对象

间接上代码:

private static final int _1MB = 1024 * 1024;    public static void main(String[] args) {        byte[] array1 = new byte[2*_1MB];        array1 = new byte[2*_1MB];        array1 = new byte[2*_1MB];        byte[] array2 = new byte[128*1024];        array2 = null;        byte[] array3 = new byte[2*_1MB];//这里触发第一次minor gc    }

JVM参数:

-XX:NewSize=10m -XX:MaxNewSize=10m -XX:InitialHeapSize=20m -XX:MaxHeapSize=20m -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10m -XX:MaxTenuringThreshold=15 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:survivor_live.log

运行代码后的日志信息:

Java HotSpot(TM) 64-Bit Server VM (25.281-b09) for bsd-amd64 JRE (1.8.0_281-b09), built on Dec  9 2020 12:44:49 by "java_re" with gcc 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5)Memory: 4k page, physical 16777216k(107748k free)/proc/meminfo:CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC 0.127: [GC (Allocation Failure) 0.127: [ParNew: 6943K->327K(9216K), 0.0030105 secs] 6943K->2377K(19456K), 0.0032581 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] Heap par new generation   total 9216K, used 2458K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)  eden space 8192K,  26% used [0x00000007bec00000, 0x00000007bee14930, 0x00000007bf400000)  from space 1024K,  32% used [0x00000007bf500000, 0x00000007bf551fb8, 0x00000007bf600000)  to   space 1024K,   0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000) concurrent mark-sweep generation total 10240K, used 2050K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000) Metaspace       used 2713K, capacity 4486K, committed 4864K, reserved 1056768K  class space    used 291K, capacity 386K, committed 512K, reserved 1048576K

接下来,咱们来剖析代码和日志:

byte[] array1 = new byte[2*_1MB];        array1 = new byte[2*_1MB];        array1 = new byte[2*_1MB];//假如这个是C对象        byte[] array2 = new byte[128*1024];        array2 = null;

这里创立了3个2m对象,1个128k的对象。最终array1指向C对象,array2置为null。对应的堆图如下所示:

此时日志所示:
ParNew: 6943K->327K(9216K)

这个时候eden区已应用6943K。

此时要执行byte[] array3 = new byte[2*_1MB];

这个时候因为可应用空间只有8m,如果要调配2m空间,此时会触发young gc。

ParNew: 6943K->327K(9216K)

因为只有array1指向了1一个2M对象,其余对象都会被回收掉。

GC后,新生代只剩下327K。这个时候你可能会问:2m对象去哪里了呢?

concurrent mark-sweep generation total 10240K, used 2050K

你看,2m对象在老年代中。为什么会在老年代中。

因为新生代调配了10m空间,而后survivor区只占1m空间。

1m的空间是无奈包容2m的对象的。

因而,对象间接进入老年代。

说到这里,咱们曾经简略 地用代码证实了:minor gc后,survivor区空间不能包容全副存活对象

有一些仔细的同学可能会问:当survivor空间有余时,存活对象都全副进入老年代吗?survivor区能包容的对象,可否放在survivor区,不能包容的对象,才放到老年代。

这个问题,咱们后续会有专门文章解答。大家先思考一下。