记一次频繁full gc的线上故障

达芬奇密码2018-08-02 10:15
某天晚上,PE突然通知说收到market服务的一直在报full gc,截图如下:


挺兄尝试重启机器未解决,于是只能深入分析到底是什么原因,直接工具脚本dump出整个现场,尝试分析jvm内存文件,无果。查看heap整体概况,发现如下:

perm已经满了?但是设置的时候明明max是256m,根本就还没有满,猜测permanent区导致的Full GC的可能性不大。不过还是重新设置了一下初始值,但不抱希望,事实证明确实是这样的。于是再次分析哨兵平台,尝试调出full gc的时间段内的接口调用情况,目的是为了定位出哪个接口可能会触发full gc的,目光转移到一个调用耗时特别长的服务接口上

于是进一步分析该方法调用的时间与full gc触发的时间,发现非常吻合,大家可以查看如下图:


gc情况如下:

这两个时间点就是同一时间,再查看了其他的gc情况也是几乎发生在同一时间,但仍然需要进一步分析出到底是什么原因出现这个问题,因为jvm内存几乎发现不了什么,于是,同学们开始陷入较长时间的代码检查,终于皇天不负有心人,黄同学通过查看一个excel组件的源码发现在关闭文件的时候该close方法显式调用了System.gc(),

我们几乎可以断定是这个地方出的问题,默默加上启动参数-XX:DisableExplicitGC,重启应用,调用后一切终于回归正常,其实,如果我们能提前知道JDK7以上的显示调用gc和隐式调用gc的区别,就应该直接能从gc日志中判断出来问题根源,如下图:

从其他的服务中看到系统隐式调用的gc会在Full GC后带有System的标示,而显示调用其实是没有的。查资料时,顺带看到一段stackoverflow上一段OpenJDK的源码代码贴一下[1]:

本文来自网易实践者社区,经作者陈鸿钦授权发布。