线上OOM异常案例及排查过程
1.导出堆转储文件分析
由于 启动脚本里面加了 -XX:HeapDumpPath=./gcLog/java_%p_%t.hprof -XX:+HeapDumpOnOutOfMemoryE
rror ,所以 发生OOM异常的时候 会自动生成 堆转储文件放到配置的指定位置。启动脚本如下:
从服务器 把 堆转储文件 down下来之后 ,用jvisualvm来分析dump文件。
jvisualvm是JDK自带的Java性能分析工具,在JDK的bin目录下,文件名就叫jvisualvm.exe。
jvisualvm可以监控本地、远程的java进程,实时查看进程的cpu、堆、线程等参数,对java进程生成dump文件,并对dump文件进行分析。像我这种从服务器上dump下来文件也可以直接扔给jvisualvm来分析。
使用方式:直接双击打开jvisualvm.exe,点击文件->装入,在文件类型那一栏选择堆,选择要分析的dump文件,打开。
用JVisualVM打开文件后就会看到提示这里有异常,点击进入这个线程异常,就可以看到报错信息
根据上面的报错信息 我们 可以定位到 程序代码的具体位置。CoreWxNoticeServiceImpl.java 文件的38 行,WxNoticeQuery.java 的 45 行。
看到这里,刚开始怀疑是不是查询出来的数据太多,导致往list 放的太多 造成OOM了。
BUT… 拿到对应的sql脚本 执行时,发现查询的出来的数据并不多,只有6百多条。
怪哉。。。。。
只能去分析对象实例了。。。。
找到真正的问题所在:
后来 ,通过查看阿里云的sql执行管理 平台,发现 服务器异常的 那个时间点,有一条sql 执行查出来了 六十多万用户数据。
找到程序的那个地方,原来那个 sql 查询 传入的是一个map ,而 map 中的值有没有 判断 空(实际业务中那里不应该为空的,为空时应该直接返回 入参为空的异常),导致查出了所有的用户数据。共60多万条,有480多M 数据(总的堆内存是1G)。
分析:
应该是这里 查询的数据太多,占了太多内存,快要接近临界值了,后面又 接着 处理其他的逻辑 ,查其他数据时(CoreWxNoticeServiceImpl.java 文件的38 行),虽然其他的数据 不多,但是 总的占用 就溢出了,所以看到报错的地方 是 CoreWxNoticeServiceImpl.java 文件的38 行 这里。
解决办法:
在执行查询用户的那个sql 之前 加 一个 判断,如果 map 里面的参数 同时为空 ,则 不往下走,直接返回 参数为空的异常。