对象何时晋升到老年代?
1. 长期存活的对象进入老年代
在Minor gc中,把age大于设置的-XX:MaxTenuringThresholed值的对象晋升到老年代。
age是这样计算的,jvm为每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并能够被Survivor区容纳的话,将被移到Survivor区中,并且对象年龄设为1。
对象在Survivor区中每“熬过”一次Minor GC,年龄就加一岁。
2. 担保机制
当Survivor区的内存大小不足以装下一次Minor Gc中所有的存活对象的时候,就启动担保机制,将Survivor不够放的活对象,直接进入到老年代。
3. 大对象直接进入老年代
虚拟机提供了个-XX:pretenureSizeThreshold参数,令内存大于这个设置值的对象直接在老年代分配。这个参数只对Serial和ParNew收集器有效,Parallel Scavenge收集器不认识这个参数,一般它也不需要设置,如果遇到必须要设置这个参数的场合,可以考虑ParNew+CMS的收集器组合。
4. 动态对象年龄判断
错误的说法:
如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
正确的说法:
1. 不是某个年龄的对象总和,而是<=某个年龄的对象总和。
2.也不一定是大于SurVivor空间的一半,只是默认-XX:TargetSurvivorRatio设为50才是一半,应该是根据这个参数才对。
gc日志分析与调优:
要在gc日志中打印出每次新生代gc后,Survivor区中的对象的年龄分布。需要增加以下jvm参数:
-XX:+printTenuringDestribution
日志如下:
2021-08-25T18:52:44.064+0800: 1139.349: [GC (Allocation Failure) 2021-08-25T18:52:44.064+0800: 1139.350: [ParNew
Desired survivor size 53673984 bytes, new threshold 15 (max 15)
- age 1: 3622408 bytes, 3622408 total
- age 2: 387696 bytes, 4010104 total
- age 3: 743552 bytes, 4753656 total
- age 4: 2206184 bytes, 6959840 total
- age 5: 2300640 bytes, 9260480 total
: 850651K->13005K(943744K), 0.0127552 secs] 1112724K->275078K(1992320K), 0.0132327 secs] [Times: user=0.09 sys=0.00, real=0.01 secs]
Desired survivor size 53673984 bytes 代表期望占用Survivor 空间的53673984 字节;
new threshold 15 (max 15) 代表jvm动态计算的对象晋升年龄为15, jvm设置的-XX:MaxTenuringThresholed对象晋升年龄为15;
jvm调优目的: 让total占用值(9260480) 大约为Desired Survivor期望值(53673984 ),即Survivor总大小的一半; 从而让gc尽可能发生在新生代, 这是为了尽量避免老年代的回收, 且任何晋升到老年代的对象都有可能造成垃圾碎片, 特别是过早晋升的。
网友评论