G1 Concurrent Refinement Thread 在干啥?
背景
公司内部基于 spring boot 开发自有组件并顺利上线,为 api 同学的开发带来了极大的便利,许多重要业务迁移到该组件, 这种情况下我们对组件的性能做一次深度压测。并对一些逻辑进行了合理优化,其中发现一些比较有意思的点。
测试环境
测试机型为 linux 4.19.84-300.el7.x86_64t 系统,配置为 4 核 cup, 8 g 内存,模拟每秒 3700 吞吐量,请求大小为 500 B。
性能问题
cpu 使用率 350%

定位问题
查看什么线程在占用 cpu ?
top -Hp 9458

发现一个线程名称为 java 线程,看一下具体是那个线程。
转换tid, 通过16进制转换 printf %x 9473,nid 号为2501
jstack 看一下具体线程在干什么 jstack 9458 > /tmp/9458

发现是 G1 的 gc 线程 在搞鬼
排查问题
G1 Concurrent Refinement Thread 这个线程是干什么的呢?
打开gc debug 功能看一下详细日志, 增加配置-XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:${LOGPATH}/gc.log

update rset 时间很长,生成大量的 Dcq 缓存,Dcq 主要是 Concurrent Refinement Thread 处理,尤其是 0# 线程是主力。
yong gc 时 rset 更新时间较长,增加参数跟踪 Refinement 线程,看一下是不是很忙。
-XX:+UnlockDiagnosticVMOptions -XX:+G1TraceConcRefinement

-XX:+G1SummarizeRSetStats -XX:G1SummarizeRSetStatsPeriod=2 查看一下 rset 占用情况

存在大量大对象时,会对 up rset 存在影响

-XX:-ReduceInitialCardMarks去掉批量更新 rset 的操作,为啥呢?
翻译一下就是,gc 希望通过批处理来减少更新 rset 时间,但是对但是对大对象不适用。我们的组件中涉及到一些压缩逻辑,需要申请大内存。

up rset 的时间变少了,是不是 G1 Concurrent Refinement Thread 也可以不那么耗时了。

果然,不再燥热了。
-XX:+ParallelRefProcEnable 开启并发引用处理

总结
JVM 在分配大对象时,会通过批处理的方式,来减少并发 rset 的更新。如果大量大对象在垃圾回收之前生成,垃圾回收时,需要在 Update Rset 阶段处理这些大对象的标记工作,会增加处理延时。使用-XX:-ReduceInitialCardMarks 可禁用这项优化。
参考文献


