一次线上故障问题排查(OOM)

早上上班,刚坐到位置上就收到线上服务挂了的异常,第一反应就是重启提供C端请求的服务器。重启也没成功,发现tomcat restart命令无效,只能通过kill -9 去关闭了服务,然后重新启动。

排查问题:

重启的过程中,在保留的一台服务器上通过jstats 命令查看gc情况,发现存在很多fullGC。通过top -Hp与jstack命令查看,占用CPU最多的也是GC线程

再通过jmap histo命令,发现存在一个异构索引的DTO数量很大... 还在排查的过程中,刚重启起来的服务又挂了。

因为刚刚看了下监控平台RPC跟HTTP的流量跟平时并无多大变化,索引DTO数量这么大。为了快速恢复线上环境,做了个大胆的猜测是MQ引起的索引构建上存在问题。安排一个同事简单改下代码,停止监听MQ(没有直接在MQ管理平台取消订阅,可以在后续继续处理堆积的消息),并重新发布。同时继续排查问题。

比较奇怪的是大量的FullGC,内存占用100% 日志中也没有OOM异常

这时先运行一个dump命令进行一遍dump一遍排查。

通过上面截图看PromoIndexDTO很大的同时,还有个  通过jstack -l命令 查看JVM中的线程,并搜索promoIndex 发现存在一个PromoIndexCreateEventHandler,到代码中查看里面存在一个List<PromoIndexDTO> 是从数据库查出的,同时没做分页操作。  在修改之前dump文件也出来了,下载到本地,通过MAT工具,也是定位到这个地方。    (幸运的是上面猜测是MQ引起的是正确的,系统也一直在正常运行)

解决方案:

将这个地方改为分页操作,通过放开MQ监听,并发布到一台线上服务。运行了一个小时确定正常,逐步发布到其它服务器。

重现问题的一个Demo:

跟线上不太一样的是,这个DEMO运行时间长一些偶尔会出现OOM(java.lang.OutOfMemoryError: GC overhead limit exceeded)异常,指示GC过于频繁(这个异常不一定出现, 运行了两个小时也没有OOM,另外也出现过一次java.lang.OutOfMemoryError: Java heap space,并最终导致进程结束的)。因为一直GC,线程也不会因为OOM被杀,所以内存一直是被栈中变量引用着(跟线上异常一样)。

为避免后续出现类似问题的优化方案:

1)加了个MyBatis拦截器,将请求返回值超过3000行的请求,发送邮件提醒。

2)近期内收到邮件及时处理相关问题,半个月后改为在发送邮件提醒的同时还要抛出异常。  对于抛出异常这个策略是否可能影响现有业务的问题团队内进行了内部沟通。对于只关心正常调用没能对异常进行有效处理的逻辑本身就应该存在设计上的问题,根据问题及早暴露原则,这种策略应该是正确的。

触发FullGC的时机

OOM:GC overhead limit exceeded

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容