王洪涛,京东商城,商家研发部架构师。丰富的构建高性能高可用大规模分布式系统的研发、架构经验。2015年加入京东,目前负责服务市场的系统研发工作。
最近分析系统经常性的报出一些可用率报警问题,跟进一个案例,报警是 API 超时,JSF(JD 自研的 RPC 框架) 提供者性能没有问题,JSF 调用者超时,分析 Full GC 造成的。
登录堡垒机,通过 jstat -gccause 命令打印 GC 原因,发现是 System.gc 导致:
查看 JVM 监控,发现个有规律的现象, Full GC 频率是1个小时。
考虑到老系统使用的是 Tomcat6,初步怀疑是 Tomcat6(之前看到过大神文章,出现过类似问题,链接:https://liuxi.name/blog/20160608/jvm-full-gc-hourly.html) 为了防止内存泄露,周期性的触发 System.gc(),下面是 server.xml 中监听器的配置。
下面是 Tomcat 6.0.33 版本中,JreMemoryLeakPreventionListener 触发 FULL GC 的实现:
Tomcat 7 提出了每隔一小时执行一次 FULL GC 这个 bug,Tomcat 7.0.28 版本中将执行频率由一小时修改成 Long.MAX_VALUE - 1。并在 Tomcat 6.0.36 版本中同步修复了这个 bug。
JDK sun.misc.GC 类的 requestLatency 方法,传入的参数表示一次 GC 请求的延迟时间。会有个低优先级的 daemon 线程执行调用 System.gc()。
防止 Tomcat 每小时 FULL GC
在 Tomcat 没修复此 bug 之前,可以通过如下方式防止每小时 FULL GC:
(1) 修改 server.xml 配置,gcDaemonProtection 参数改为 false,默认是 true
(2) 修改server.xml配置,去掉JreMemoryLeakPreventionListener监听器
(3) Tomcat 已经修复了此 bug,升级 tomcat 版本也可以解决每小时执行一次 FULL GC 问题
我们采用方法1,线上验证不再进行定时的 Full GC。
为什么 Full GC 影响了 API 超时
我们看下 JVM 的配置内容,Young GC 是 PS Scavenge,Full GC 是 PS MarkSweep。
FULL GC 会导致 stop-the-world,频繁的 FULL GC 会影响系统的可用性。从最开始的图看到 Full GC 的平均耗时 2733 ms,FULL GC 时间过长,会让负载均衡器觉得各个机器服务不可用,可能导致整个服务下线。
参考:
https://liuxi.name/blog/20160608/jvm-full-gc-hourly.html