糟了,线上服务出现OOM了

前言前一段时间,公司同事的一个线上服务OOM的问题,我觉得挺有意思的,在这里跟大家一起分享一下 。
我当时其实也参与了一部分问题的定位 。
1 案发现场他们有个mq消费者服务,在某一天下午,出现OOM了,导致服务直接挂掉 。
当时我们收到了很多内存报警邮件
发现问题之后,运维第一时间,帮他们dump了当时的内存快照,以便于开发人员好定位问题 。
之后,运维重启了该服务,系统暂时恢复了正常 。
大家都知道,如果出现了线上OOM问题,为了不影响用户的正常使用 , 最快的解决办法就是重启服务 。
但重启服务治标不治本,只能临时解决一下问题,如果不找到真正的原因 , 难免下次在某个不经意的时间点,又会出现OOM问题 。
所以,有必要定位一下具体原因 。
2 初步定位问题当时运维dump下来的内存快照文件有3G多,太大了,由于公司内网限制,没办法及时给到开发这边 。
没办法,只能先从日志文件下手了 。
在查日志之前 , 我们先查看了prometheus上的服务监控 。查到了当时那个mq消费者服务的内存使用情况,该服务的内存使用率一直都比较平稳,从2022-09-26 14:16:29开始,出现了一个明显的内存飙升情况 。
根据以往经验总结出来的,在追查日志时,时间点是一个非常重要的过滤条件 。
所以,我们当时重点排查了2022-09-26 14:16:29前后5秒钟的日志 。
由于这个服务,并发量不大,在那段时间的日志量并不多 。
所以 , 我们很快就锁定了excel文件导入导出功能 。
该功能的流程图如下:

糟了,线上服务出现OOM了

文章插图
  1. 用户通过浏览器上传excel,调用文件上传接口 。
  2. 该接口会上传excel到文件服务器 。然后将文件url,通过mq消息,发送到mq服务器 。
  3. mq消费者消费mq消息,从文件服务器中获取excel数据 , 做业务处理,然后把结果写入新的excel中 。
  4. mq消费者将新excel文件上传到文件服务器,然后发websocket消息通知用户 。
  5. 用户收到通知结果,然后可以下载新的excel 。
经过日志分析 , 时间点刚好吻合,从excel文件导入之后,mq消费者服务的内存使用率一下子飙升 。
3. 打不开dump文件从上面分析我们得出初步的结论,线上mq消费者服务的OOM问题 , 是由于excel导入导出导致的 。
于是,我们查看了相关excel文件导入导出代码,并没有发现明显的异常 。
为了找到根本原因,我们不得不把内存快照解析出来 。
此时,运维把内存快照已经想办法发给了相关的开发人员(我的同事) 。
那位同事用电脑上安装的内存分析工具:MAT(Memory Analyzer Tool),准备打开那个内存快照文件 。
但由于该文件太大,占了3G多的内存,直接打开失败了 。
糟了,线上服务出现OOM了

文章插图
MemoryAnalyzer.ini文件默认支持打开的内存文件是1G,后来它将参数-xmx修改为4096m
修改之后,文件可以打开了,但打开的内容却有问题 。
猛然发现,原来是JDK版本不匹配导致的 。
他用的MAT工具是基于SunJDK , 而我们生成环境用的OpenJDK,二者有些差异 。
SunJDK采用JRL协议发布,而OpenJDK则采用GPL V2协议发布 。两个协议虽然都是开放源代码的,但是在使用上的不同,GPL V2允许在商业上使用,而JRL只允许个人研究使用 。
所以需要下载一个基于OpenJDK版本的MAT内存分析工具 。
4. 进一步分析刚好,另一个同事的电脑上下载过OpenJDK版本的MAT内存分析工具 。
把文件发给他帮忙分析了一下 。
糟了,线上服务出现OOM了

文章插图
最后发现org.apache.poi.xssf.usermodel.XSSFSheet类的对象占用的内存是最多的 。
糟了,线上服务出现OOM了

文章插图
目前excel的导入导出功能,大部分是基于apachePOI技术 , 而POI给我们提供了WorkBook接口 。
常用的WorkBook接口实现有三种: