记录一次线上系统OOM问题的解决过程

前段时间跳槽了,刚入职没3、4个月,我所在的小组主要负责DSP系统,我主攻DSP中的dist-orders服务

先说一下DSP是干嘛的,项目组所在的公司主要是从事医药行业的,在淘宝、天猫、京东......很多渠道都有自家的店铺,他们自己还有一套ERP,主要是进销存那一套,DSP就是负责连接ERP和各个渠道之间传递信息的,什么订单啦、库存啦......

前几周,leader收到线上某某某个机子系统OOM的邮件,leader就随手丢给我,让我去没事的时候去查查

那我直接登堡垒机去服务器上看看,各种乱七八糟命令就开始用,什么top、jstack、jinfo、jstat、jmap...结果发现一个都不行,不是命令不行,是我没权限查看......

不说我只不知道该申请什么样子的权限,就算是知道了,运维也已经重启完了,那还查个屁

最终希望只能放在堆转储文件上,堡垒机是可以下载服务器上的文件,奈何文件太大,6G多,根本下不下来,寻求运维组的同事帮忙,各种发邮件、拉群、最终在我的软磨硬泡下,终于是给到了我

废话不多说,直接上jdk自带的jvisualvm软件进行分析,(jvisualvm在你安装jdk的bin目录下)

交给jvisualvm进行解析分析,我电脑是16G内存,分析6G的堆转储文件,差点没给我电脑送走......
记录一次线上系统OOM问题的解决过程_第1张图片

这里除了导致OOM线程 和 系统属性之外 没什么太重要的信息
点开 显示系统属性 发现JVM也没设置什么参数(最大堆、最小堆、GC一些参数等等)
我们来看 类
记录一次线上系统OOM问题的解决过程_第2张图片

发现char[]和String占了大头,双击char[],进去看看
记录一次线上系统OOM问题的解决过程_第3张图片
emmm,不知道怎么回事有的地方是乱码,而且暂时也没看到有用的信息

点过来点过去,看了好久,最终决定换个软件

用IBM heapanalyzer试试(下载地址:https://www.ibm.com/support/pages/node/1109955?mhsrc=ibmsearch_a&mhq=heapanalyzer

是个jar包,直接java -jar -Xmx15g xxx.jar启动(这里的-Xmx多少内存自己定,如果分析过程中OOM就是你给的内存太少),或者你可以随便写个启动的脚本

将堆转储文件载入进来,最终就是这个样子了
记录一次线上系统OOM问题的解决过程_第4张图片

他这里有个 疑似泄露的点,是DisOrderItemBO对象,占了2.8G内存,约58.74%

再往后下看ArrayList,大约猜测就是ArrayList装载了太多的DisOrderItemBO

让他以树结构形式展示:
记录一次线上系统OOM问题的解决过程_第5张图片

没跑了,就是ArrayList集合内DisOrderItemBO元素太多,看解析是有1936459个DisOrderItemBO的实例,总共占用了2G多内存

看似得到结果了,实则还是不知道从哪下手,因为DisOrderItemBO用到的地方太多了,根本不知道是哪个地方引发的

于是,我又又又换了一个软件

MemoryAnalyzer(下载地址:https://www.eclipse.org/downloads/download.php?file=/mat/1.12.0/rcp/MemoryAnalyzer-1.12.0.20210602-win32.win32.x86_64.zip

加载进来就是这个样子了
记录一次线上系统OOM问题的解决过程_第6张图片

这里也有一个 疑似内存泄漏的点,点击去查看,列出了详细的信息:
记录一次线上系统OOM问题的解决过程_第7张图片

首先是哪个线程出现的问题,其次是哪些对象导致的内存增长且回收不掉

发现和上一个软件分析的结果差不多,都是ArrayList集合内DisOrderItemBO元素太多

现在是已知DisOrderItemBO对象产生的太多(在一个线程内),且回收不掉,ArrayList直接就抛弃掉了,因为他本身没问题,只是承载了太多的DisOrderItemBO对象,现在我只需要知道是哪个线程,刚好这个软件可以看(或许前两个也能看,我没找到吧)
记录一次线上系统OOM问题的解决过程_第8张图片

http-nio-7210-exec-9线程,我没记错的话,应该是Tomcat任务线程池的线程吧,现在又可以确定一件事,就是导致OOM的肯定不是系统中的异步任务或者是什么JOB等等,其实这里已经给出了答案,怪就怪在我懒,没往下继续看

线程信息里面给出了我的Service相对应的类的全包名以及对应的controller的全包名

根据信息直接找到代码,稍微捋了一下代码逻辑,在本地启动试试,我去线上日志摸了一个请求报文,用postman请求一下,试了几次怎么玩都不会出现很多DisOrderItemBO对象

然后我就想,如果参数为空(原本代码就不会对必填参数校验),我就试了一下,控制台打出sql之后,我拿着去线上DB中执行这个sql

去掉不必要的查询列,以及排序,只看个数(一条数据对应一个DisOrderItemBO示例),同理如果查出来了前面分析的1936459个示例,那么就对上了

为了严谨,我在where条件多了一个创建时间,时间截止到这个堆转储文件产生的前10分钟(发生OOM时产生的堆转储文件会很慢)结果查询出来了1936374个条数据,虽然没对上,但是误差也没有很多,现在看来,大差不差就是这里除了问题了

虽然找到了,但是疑问点是,这个接口是查询订单详情,在页面上操作不应该出现没有必填参数(订单主体ID)的情况呀,这个疑问我一直没想通......

如果过了几周半个月的,我没有更新后续继续排查的文章,就说明已经把这个问题完美解决了

你可能感兴趣的