2020年03月18日,开年第一个生产事故发送在我的项目组o(╥﹏╥)o
事故发生
在下午2点40时,收到了第一个客户投诉,紧接着收到了第二个、第三个。
并且不断的有各业务系统表示自己的系统出现了操作无响应的现象,如此大范围的影响,推测大概率是网关的功能出现了问题。
事故定位
立马上日志平台搜索Nginx处的请求日志,果然在Nginx上发现了大量的499响应码日志,紧接着拿着top 1请求量的499接口来搜索安全网关的日志,发现了大量的:java.lang.OutOfMemoryError: GC overhead limit exceeded
,由于怕过年期间生产不稳定,从年前开始,已经很长时间都没有发布了,所以排除了新增的代码BUG。
解决
那么就有可能是长时间运行慢慢堆积的对象导致的OOM,直接重启安全网关,重启后成功解决。
覆盘
java.lang.OutOfMemoryError: GC overhead limit exceeded
根据官方的解释是在多次gc后效果极差抛出的OOM。
怀疑可能存在内存泄漏,直接分析OOM后的dump文件。
从Dominator Tree图中排出占用内存最大的top n对象,挨个查看它的GC root:
再切换到Histogram,同样排出占用内存最大的top n对象,挨个查看GC root。
调优
分析后发现skywalking和spring产生了大量的Class对象,skywalking产生了大量的Endpoint对象,spring产生了大量的动态代理对象。并没有自身代码产生的内存泄漏对象。
- 适当的调大JVM堆大小
- 进一步调查Endpoint对象,是否可以控制过期时间。
事故的调查期间还发现了服务自启动以来只有过2次full gc,都是在启动初期由于Metaspace空间不足扩容导致的full gc,所以还需要调整Metaspace空间。
-XX:MetaspaceSize=128M
另外,我还调低了MaxTenuringThreshold
,这个参数影响进入老年代的年龄阈值。
因为网关的对象都是请求级别的生命周期,分析gc日志可以知道young gc平均在30s执行一次,MaxTenuringThreshold
的默认值是15,会导致部分长期存活对象需要将近8分钟的时间才能进入老年代,本来年轻代的空间就不大,再被这些长期存活对象一占用,会使得young gc更加频繁。