背景
现网服务频繁YGC,每次YGC都会有对象晋升老年代,造成频繁混合回收。但是程序中接口执行很快,平均响应时长在10ms,理论上不该产生老年代对象。
到底什么对象在晋升?如果能够定位晋升对象,我们就可以对程序做进一步的优化,下面我们来介绍在G1垃圾收集器中如何快速定位YGC后晋升到老年代中的对象。
实践
环境:java11
启动脚本增加参数:
-XX:+UseG1GC -Xlog:gc*,region*=trace:./gc.log:time
在程序进行混合回收前打DUMP
注意:建议在打印dump前先压缩gc日志,否则dump后产生的GC日志可能会影响排查过程。-
查找有老年代对象晋升的YGC
通过脚本grep "Old regions:" gc.log
查看 old region变化
发现第24次GC有大量对象晋升
对象晋升一定会分配新的老年代分区,使用命令查询第24次GC老年代的分配情况
命令:grep 'GC(24)' gc.log | grep 'ALLOC(OLD)'
,用于查找第24次GC分配的老年代分区。
-
我们根据内存地址查询第24次GC的分配和使用情况
命令:
grep 'GC(24)' gc.log | grep '0x00000007c3200000'
图中可发现此region从F变为了O(空闲region转变为老年代region),表示有对象晋升到此region。region地址:0x00000007c3200000,0x00000007c3300000
我们可以使用相同的方法查看其它region的晋升情况,并找到晋升的内存地址。
注意:[] 中的三个16进制数,分别为region的起始地址、当前已使用到的地址、结束地址
- 使用MAT工具打开DUMP文件,并使用OQL根据region地址进行查找
命令:SELECT * FROM INSTANCEOF java.lang.Object t WHERE toHex(t.@objectAddress)>="0x7c3300000" AND toHex(t.@objectAddress)<="0x7c3400000"
注意:查找条件中的内存地址需要将前面连续的0去掉。