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 的操作,为啥呢?
Spurious high Update RS times in combination with the application allocating large objects may be caused by an optimization that tries to reduce concurrent remembered set update work by batching it. If the application that created such a batch happens just before a garbage collection, then the garbage collection must process all this work in the Update RS times part of the pause. Use -XX:-ReduceInitialCardMarks to disable this behavior and potentially avoid these situations.
翻译一下就是,gc 希望通过批处理来减少更新 rset 时间,但是对但是对大对象不适用。我们的组件中涉及到一些压缩逻辑,需要申请大内存。
up rset 的时间变少了,是不是 G1 Concurrent Refinement Thread 也可以不那么耗时了。
果然,不再燥热了。
-XX:+ParallelRefProcEnable 开启并发引用处理
总结
JVM 在分配大对象时,会通过批处理的方式,来减少并发 rset 的更新。如果大量大对象在垃圾回收之前生成,垃圾回收时,需要在 Update Rset 阶段处理这些大对象的标记工作,会增加处理延时。使用-XX:-ReduceInitialCardMarks 可禁用这项优化。
参考文献
版权声明: 本文为 InfoQ 作者【BUG侦探】的原创文章。
原文链接:【http://xie.infoq.cn/article/68597709c4131a306af6a6322】。文章转载请联系作者。
评论