对象进入老年代的四种方式
- 大对象
- 动态年龄判断
- 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区,不能容纳的对象,才放到老年代。
这个问题,我们后续会有专门文章解答。大家先思考一下。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。