对象进入老年代的四种方式
- minor gc后,survivor区空间不能容纳全部存活对象
- 存活对象达到年龄阈值。比如15
- 大对象
- 动态年龄判断
大对象
首先,我们简单回顾一下。
书本,也没说明,多大的对象才是大对象,比较抽象。
我们这里直接具体点:
-XX:PretenureSizeThreshold=3m
大于等于3m的对象,就是大对象。
JVM配置参数
-XX:NewSize=10m -XX:MaxNewSize=10m -XX:InitialHeapSize=20m -XX:MaxHeapSize=20m -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3m -XX:MaxTenuringThreshold=15 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:bigobject.log
代码:
byte[] array1 = new byte[2*_1MB];
byte[] array2 = new byte[3*_1MB];
因为2m的对象不是大对象,因此分配到eden区。而3m的对象是大对象,因此分配到old区。
上面,这些都是我们根据理论来推测出来的。
接下来,我们运行一下代码,然后查询日志文件bigobject.log
Java HotSpot(TM) 64-Bit Server VM (25.261-b12) for bsd-amd64 JRE (1.8.0_261-b12), built on Jun 18 2020 06:38:55 by "java_re" with gcc 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5)
Memory: 4k page, physical 33554432k(382392k 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=3145728 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
Heap
par new generation total 9216K, used 4849K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
eden space 8192K, 59% used [0x00000007bec00000, 0x00000007bf0bc580, 0x00000007bf400000)
from space 1024K, 0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
to space 1024K, 0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
concurrent mark-sweep generation total 10240K, used 3072K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3074K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 337K, capacity 388K, committed 512K, reserved 1048576K
现在,我们分析一下日志内容,来验证我们的猜测是否正确。
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=3145728 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
Heap
上面这一堆,就是我们自己设置的JVM参数,也有一些系统帮我们添加的。比如:-XX:+UseCompressedClassPointers -XX:+UseCompressedOops
这些不是重点,你了解一下就好。
重点是这个:
Heap
par new generation total 9216K, used 4849K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
eden space 8192K, 59% used [0x00000007bec00000, 0x00000007bf0bc580, 0x00000007bf400000)
from space 1024K, 0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
to space 1024K, 0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
concurrent mark-sweep generation total 10240K, used 3072K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
代表的含义是:GC后堆的情况,注意是GC后。这里特别强调一下。
par new generation total 9216K, used 4849K
代表的新生代,现在总的空间大小是9216K,已使用的空间大小是4849K。
其实我们知道,有2M对象是进入了eden区的,但是现在4849K明显是大于2048K(2M)的。
那说明了什么?
说明,其实JVM除了加载我们自己写的对象外,还会加载一些其他未知对象。未知对象,主要由JVM本身产生,这部分大家先忽略就好
回到主要问题上:大对象是否会直接进入老年代。
我们看以下这段日志:
concurrent mark-sweep generation total 10240K, used 3072K
老年代的总空间大小是10240K,目前已经使用了3072K。
看到这里,其实相信大家已经可以明白了。我们把大对象定义为大于等于3m的对象。而日志也告诉我们,目前老年代已经被占用了3072K,即3m。
因此,到这里我们已经用代码验证了:大对象会直接进入老年代。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。