一、故障发生
从一周前开始陆续接到线上用户反馈说接口访问偶发延时过大,频率大概是10%,延时8s左右。通过查看日志发现,有时上游服务发出请求后七八秒钟后本服务才收到请求,有时是本服务发出请求后七八秒钟后下游服务才收到请求,加之本服务已经在线上4个月没修改过代码没重启了,猜测可能是网络原因。找运维看了好几天也没发现大问题。
二、问题排查
1. 发现线索
偶然翻看线上gc日志,发现隔几分钟就会出现concurrent mode failure错误,处理这个错误花费的时间恰好跟线上接口延时时间相符。日志如下:
2020-03-29T07:18:23.553+0800: 11637468.523: [GC2020-03-29T07:18:23.553+0800: 11637468.523: [ParNew: 628867K->628867K(629120K), 0.0000410 secs]2020-03-29T07:18:23.553+0800: 11637468.
523: [CMS2020-03-29T07:18:24.060+0800: 11637469.029: [CMS-concurrent-sweep: 0.760/0.763 secs] [Times: user=0.00 sys=0.82, real=0.76 secs]
(concurrent mode failure): 1398143K->1398143K(1398144K), 8.9975520 secs] 2027011K->1869203K(2027264K), [CMS Perm : 43504K->43504K(72824K)], 8.9978420 secs] [Times: user=0.00 sys=8.98, real=9.00 secs]
出现这个错误是因为在CMS回收老年代垃圾的时候,又有新的对象要进入老年代,但这时老年代空间不足放不下,触发这个报错。这时JVM会改用serial收集器回收垃圾,速度很慢且会stop the world。于是怀疑代码里可能有哪里有大对象产生。查看JVM参数如下:
-Xmx:2048M -XX:MaxPermSize=512M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps \
-Xloggc:gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=20M
按照默认1:2计算,