OOM排查思路
1. 什么是OOM
程序的内存不够了,挂掉了
2. OOM的原因
oom的原因其实就一个,内存不够了,我们要细分的就是哪里的内存不够了,以及为什么内存不够了
2.1 栈溢出
少见。大部分都因为递归。
2.2 方法区溢出
方法区存的都是类装载的静态结构,那什么情况会造成这种现象呢?
- 如果是系统启动类加载的时候出现OOM,那说明类太多了,方法区内存不够用了,就考虑是不是加大方法区的内存或者干脆加大JVM的大小,或者反向思考,是不是类真的太多了?是否需要拆服务?
- 如果是运行中出现这种OOM,那说明运行的时候,可能有太多类加载进去了,没错,就是反射,或者说再偶然一点,本地环境启动时,如果热部署新增太多的类也会占用方法区内存。毕竟,不管是反射还是热部署,都是破坏了双亲委派机制。
2.3 堆溢出
其实,大部分OOM场景都是堆溢出,也就是对象满了,来不及GC,内存溢出不够分配了。
那么堆中存放的又是什么呢?当然是实例对象
- 可能是代码中有人写了死循环创建对象,导致一瞬间创建的对象太多了
- 某个对象的内存占用无限增长,发生了内存泄漏。比如某个service中使用了ThreadLocal但是事后没有remove,导致它维护的ThreadLocalMap越来越大。最终溢出。
3. 发生OOM该怎么办
如果你发现服务挂掉了,去看日志发现是OOM
- 首先肯定是尝试重启,并且随时观察服务运行状态。如果暂时能运行,那自然是最好的,而且会排除一些情况。
- 不管服务起没起来,我们都要进行最关键的一步,分析为什么会出现OOM
拿什么分析?自然需要有日志之类的记录–这里指的就是heap dump
4. heap dump
4.1 什么是heap dump
Heap dump文件是一个二进制文件,它保存了某一时刻JVM堆中对象使用情况。Heap Dump文件是指定时刻的Java堆栈的快照,是一种镜像文件。Heap Dump一般都包含了一个堆中的Java Objects, Class等基本信息。同时,当你在执行一个转储操作时,往往会触发一次GC,所以你转储得到的文件里包含的信息通常是有效的内容(包含比较少,或没有垃圾对象了) 。我们可以这么理解:heap dump记录内存信息的,thread dump是记录CPU信息的。
4.2 怎么得到heap dump
- 事先开启HeadDumpOnOutOfMemoryError,这样出现OOM的时候能自动留下Dump,留好第一现场。这是最推荐的方式。
JVM的启动参数中加入如下的一些参数:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/usr/local/oom
第一个参数意思是在OOM的时候自动dump内存快照出来,第二个参数是说把内存快照存放在哪里
- 手工得到heap dump,这个方式就很多了,命令或者一些第三方工具,都可以获得指定java进程的heap dump。比如jdk自带的jvisualvm.exe,这个是图形化的也不用额外下载。其他方式可以自己去,有很多。
通过jvisualvm.exe获取heap jump.