1 问题描述
内存溢出(Out Of Memory,简称OOM)是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。此时程序就运行不了,系统会提示内存溢出,有时候会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件,而由系统配置、数据流、用户代码等原因而导致的内存溢出错误,即使用户重新执行任务依然无法避免。
java.lang.OutOfMemoryError error is thrown when there is insufficient space to allocate an object in the Java heap.
典型特征:控制台等日志会出现“java.lang.OutOfMemoryError:XXXX”异常日志。
常见原因:大对象一次性分配一个大的内存空间、内存泄漏、JVM设置不当等。
2 故障排查
2.1 整体思路
整体思路:根据OOM类型明确溢出的内存区域+明确是否有泄漏+分析泄漏/对象生命周期/存储结构/环境检查等。
(1)根据控制台日志(**/jstack/runtime/log/loging.log)查看OOM具体信息。如:创建对象时分配的空间大于堆内存连续可用空间时导致的java.lang.OutOfMemoryError: Java heap space;进程98%以上的时间在执行GC,且连续5次GC回收空间在2%以内导致的java.lang.OutOfMemoryError: GC Overhead limit exceeded;MaxMetaspaceSize设置太小,且加载到Metaspace的class太多或太大导致的java.lang.OutOfMemoryError: Metaspace;栈帧太大或虚拟机栈容量太小导致新的栈帧内存无法分配时报出的StackOverflowError。
(2)通过start-jstack.sh启动文件的JVM配置参数,或jcmd PID VM.flags命令可以查看JVM内存分配情况。
(3)抓取OOM时刻dump,结合控制台日志、环境配置、内存对象等信息明确OOM原因。
2.2 实施方案
2.2.1 抓取coredump
JVM参数中添加-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump/store,在出现OOM时即会自动抓取问题时刻coredump。(后续会在JVM参数中默认设置自动抓取OOM时刻dump)
2.2.2 coredump分析
MAT分析dump文件,结合dump分析的对象、线程、Leak Suspects以及JVM参数等信息分析出现OOM的原因。
一般思路,根据top对象明确相关的线程堆栈,根据线程堆栈明确业务场景,开发人员结合业务场景做进一步分析。
以MAT(https://www.eclipse.org/mat/ )为例,打开MAT,添加待分析文件File-Open Heap Dump…,选择前面收集的dump文件之后即默认开始分析。
注意:MAT默认分配内存过小,在分析dump时会出现OOM,一般在分析生产环境coredump时,需要先调整MAT内存参数。具体方法为,修改工具目录下的MemoryAnalyzer.ini文件,添加-Xmx12024m参数,可以结合工具所在机器情况,将MAT最大内存调大(如限制为物理内存的一半或80%)。
如下示例场景为,类加载过多导致的GC Overhead limit exceeded类OOM coredump。Leak Suspects report中,嫌疑的泄漏内容为线程org.apache.tomcat.util.threads.TaskThread @ 0xeef66240 http-nio-8080-exec-1的"javassist.ClassPool"占用了215,708,456 (占堆内存的92.21%)。之后点击“See stacktrace”或“Details”即可查看内存泄漏嫌疑内容相关的线程堆栈和详细内存信息。
2.2.3 几个技巧
(1)检查物理内存及JVM内存参数,JVM内存小于4G时,通常建议先增加JVM内存分配。
(2)重点关注TOP对象,如某对象数较其他在数量或占用大小明显高于其他对象时,top内容一般为高嫌疑内容。
(3)在分析TOP内容时,重点关注产品相关内容。
3 解决方案
调整JVM参数、优化内存泄漏代码、优化大对象等。