背景
前段时间,生产环境服务器出现了OOM,并没有什么吃内存的功能,服务器分配的内存余量还是很大的,为什么会这样呢?程序出现OOM,自然是程序中申请内存没有释放也就是通常所说的内存泄漏,或者申请的内存超出了JVM能提供的内存大小,此时称之为内存溢出。那么如何查看占用内存多的地方在哪里呢?
-XX:+HeapDumpOnOutOfMemoryError
线上环境运行JVM进程时,建议加上此参数,目的是在JVM发生OOM时自动生成内存快照。
Eclipse Memory Analyzer
1、从MAT的分析报告中我们看出,org.apache.tomcat.util.threads.TaskThread @ 0x6e5a26f40 http-nio-8080-exec-39 这个线程持有的本地变量多达3.9GB。
2、通过List object with outgoing 可以看到是哪个reference占用了如此多的内存。
这个图中可以看出一个com.mysql.jdbc.ByteArrayRow的数组几乎占满了 3.9GB的空间,推断应该是某个sql查询判断条件没有加号,查出了太多的记录,还未来得及转化成对应的object就已经OOM了。利用thread_overview可以看到是哪个接口导致了这个情况。
thread_overview 页
通过thread_overview页,可以查看发生OOM时此线程的调用堆栈。
一个很深的调用堆栈,找到程序中的代码片段,就定位到了具体的方法。
展开方法名,是可以看到调用参数的。最后定位到问题,是程序中新加的方法传了一个空字符串作为参数,在判断中漏掉了,最终把满足所有条件的数据查了出来,导致OOM。
到此为止,已经找出原因,最终系统及时修复并上线。搞定OOM的三板斧大概就是这样,非常简单,但很实用。