线上OOM问题实战

项目在测试环境运行一段两天后,会出现OutOfMemoryError: Java heap space 的错误,导致程序崩溃停止
遇到这种错误,往往很难知道是哪里出错,我去年有记录定位OOM的问题的文章,刚好重温一下:线上java.lang.OutOfMemoryError问题定位三板斧

先不着急重启,先看看内存情况:

  • top 查看java运行的pid
  • jmap -heap pid 查看内存占用情况,新生代和eden区确实是用了100%
  • jmap -histo:live pid| more 查看最大对象,发现是[C] 最大,也就是char

可是这样还是不知道哪里出了问题,因为程序用到字符的特别多,这里就需要更精细的定位工具,我用的是mat

  • 到官网下载最新的mat
  • 获取项目运行的heap_dump文件
  • 在本地导进heap_dump文件分析定位,查找内存泄露的地方

1.下载mat地址:https://www.eclipse.org/mat/downloads.php,下载完解压即可

线上OOM问题实战

2.获取项目运行的heap_dump文件
在服务器随意目录下,执行:jmap -dump:live,format=b,file=heap-dump.bin pid
便会在该路径下生成heap_dump文件,我这里有5G,生成文件需要点时间,完后再下载到本地

3.运行mat的eclipsec.exe, 会弹出一个mat的界面,点击左上角的file - Open Heap Dump 选择刚刚下载的heap_dump文件,由于比较大,导进也需要点时间,然后就会来到这个界面:

线上OOM问题实战
可以看到内存的占比,其中有一个占用了5G!基本可以判定是内存不释放,堆积到耗尽内存空间,点击下边最左边的Histogram(这是查看条状分布的)

线上OOM问题实战
从上图可以看到,果然是char最大,这四列的意思:class Name是类名,Objects是有多少个对象,Shallow Heap 是占用的内存,Retained Heap是GC后可以释放的内存,我重点在定位char,在char[]上右键 - List Objects - with incoming references

线上OOM问题实战
查看incoming references是为了看char在哪些地方被引用,选择第一行右键 - Path To GC Roots - exclude all…

线上OOM问题实战
最后就可以看到在代码中的位置了

线上OOM问题实战

然后仔细一看这部分的逻辑,确实有一个haspMap,不怎么常用,但是put和get后,没有删除元素,加了remove 操作,已经运行一天,jamp 监控内存没发现堆积的,应该是解决了。

除了要记得删除map中不必要的元素外,要有一些可以优化的方法,延伸:

  • 尽量不要在循环内new对象,在循环外置为null,在循环内 = new XXX(); 即可, 而且,循环结束后,让对象 = null, 暗示gc回收
  • system.gc() 只是会提醒jvm去回收,但是会不会马上执行,是由于jvm决定的,当然可以通过配置jvm参数调整
  • 单例模式,尽量不要引用外部的引用,外部引用有可能不会被回收,最好将所有变量的生命周期在本作用域
  • 出了问题不要慌,要冷静!!! 心态很重要,如果条件允许,不用立马恢复,先保存好错误日志,以便排查