常见java OOM异常分析排查思路分析

浪里小白龙59 2024-09-08 12:35:01 阅读 98

Java 虚拟机(JVM)发生 OutOfMemoryError(OOM)异常时,表示 JVM 在尝试分配内存时无法找到足够的内存资源。以下是几种常见的导致 OOM 异常的情况:

1. Java 堆空间不足 (Java Heap Space)

这种情况发生在 JVM 堆内存耗尽,无法再为新的对象分配空间。

原因

创建了大量对象且无法及时被垃圾回收。内存泄漏:对象持有引用无法被垃圾回收。内存中缓存过多数据。

解决方案

调整 JVM 堆内存大小(增加 -Xmx 参数)。优化代码,减少内存消耗。检查并修复内存泄漏。

Java 堆溢出排查解决思路

1.查找关键报错信息,比如

java.lang.OutOfMemoryError: Java heap space

2.使用内存映像分析工具(如Jprofiler)对Dump出来的堆储存快照进行分析,分析清楚是内存泄漏还是内存溢出。

这里给出我安装整合idea参考的教程

JProfiler 11 安装与破解 - 哑吧 - 博客园

Intellij IDEA集成JProfiler性能分析神器-CSDN博客

3.如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,修复应用程序中的内存泄漏。

4.如果不存在泄漏,先检查代码是否有死循环,递归等,再考虑用 -Xmx 增加堆大小。

demo代码:

<code>import java.util.ArrayList;

import java.util.List;

public class HeapOOM {

static class OOMObject {

}

public static void main(String[] args) {

List<OOMObject> list = new ArrayList<OOMObject>();

//在堆中无限创建对象

while (true) {

list.add(new OOMObject());

}

}

}

按照排除解决方案。

1.查找报错关键信息

image.png

<code>Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

2.使用内存映像分析工具Jprofiler分析产生的堆储存快照

(1)我们可以先通过 top -c查看当前服务器进程并记录当前消耗cpu最高线程的pid。

在这里插入图片描述

比如发现当前线程pid为744的使用率最高。

(2)然后通过下面的命令到处jvm内存快照

<code>jmap -dump:formart=b.file=java_pid_744.hprof 744

(java_pid_744.hprof是文件名。 744是通过top c查看消耗cpu使用率最高的线程id)

然后下载到本地,下载先可以先压缩一下,这样可以节省时间。一个小技巧。

(3)使用上面下载好的JProfiler打开生成的单个快照

在这里插入图片描述

OOMObject这个类创建了11956010个实例,是属于内存溢出

然后点击这个最大对象分析

image.png

然后我这时候电脑卡着了,借用网图给接下来步骤说明

打开后右键打开使用选定对象

image.png

在这里插入图片描述

然后这里会显示详细的日志

image.png

这里可以看见具体的代码块。然后我们就可以定位代码结合具体代码进行分析。发现死循环了。

image.png

2.线程栈空间不足 (Stack Overflow)

关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常:

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError 异常。

原因

在单个线程下,栈帧太大,或者虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出StackOverflowError 异常。不断地建立线程的方式会导致内存溢出。

解决方案

优化代码,避免过深的递归调用。调整线程栈大小(增加 -Xss 参数)。

栈溢出排查解决思路

查找关键报错信息,确定是StackOverflowError还是OutOfMemoryError

如果是StackOverflowError,检查代码是否递归调用方法等

如果是OutOfMemoryError,检查是否有死循环创建线程等,通过-Xss降低的每个线程栈大小的容量

demo代码

<code>public class JavaVMStackOOM {

private void dontStop() {

while (true) {

}

}

public void stackLeakByThread() {

while (true) {

Thread thread = new Thread(new Runnable() {

public void run() {

dontStop();

}

});

thread.start();

}

}

public static void main(String[] args) {

JavaVMStackOOM oom = new JavaVMStackOOM();

oom.stackLeakByThread();

}

}

1.报错信息

Exception in thread “main” java.lang.OutOfMemoryError: unable to create new native thread

2.定位dontStop 方法是一个无限循环,线程一旦执行这个方法,将会一直循环下去

3.排查代码,确定是否显示使用死循环创建线程

3.方法区溢出

方法区,(又叫永久代,JDK8后,元空间替换了永久代),用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。运行时产生大量的类,会填满方法区,造成溢出。

方法区溢出原因

使用CGLib生成了大量的代理类,导致方法区被撑爆

在Java7之前,频繁的错误使用String.intern方法

大量jsp和动态产生jsp

应用长时间运行,没有重启

方法区溢出排查解决思路

调整元空间大小(增加 -XX:MaxMetaspaceSize 参数)

检查代码是否频繁错误得使用String.intern方法

优化类加载机制,减少不必要的类加载,检查是否使用CGLib生成了大量的代理类

重重启JVM

4.本机内存不足 (Native Memory Exhaustion)

这种情况发生在本机内存耗尽时。

原因

本机代码分配了大量内存(如 JNI 调用)。内存泄漏。

解决方案

检查并优化本机代码。确保本机内存使用合理。

比如:

NIO程序中,使用ByteBuffer.allocteDirect(capability)分配的是直接内存,可能导致直接内存溢出。

image.png

ByteBuffer分配128MB直接内存,而JVM参数-XX:MaxDirectMemorySize=100M指定最大是100M,因此发生直接内存溢出。

5.GC 造成的内存不足 (GC Overhead Limit Exceeded)

这种情况发生在垃圾回收频繁且回收效果不明显时(超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。)。

原因

程序创建对象过快,垃圾回收无法跟上。内存不足,垃圾回收无法有效清理。

解决方案

检查JVM参数-Xmx -Xms是否合理检查项目中是否有大量的死循环或有使用大内存的代码,优化代码。增加 JVM 堆内存大小。优化代码,减少对象创建速度。使用更高效的垃圾回收器(如 G1 GC)。

demo

<code>public class GCoverheadTest {

public static void main(String[] args) {

ExecutorService executor = Executors.newFixedThreadPool(10);

for (int i = 0; i < Integer.MAX_VALUE; i++) {

executor.execute(() -> {

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

//do nothing

}

});

}

}

}

任务积压:线程池的大小是 10,这意味着同时最多只有 10 个任务在执行。其余的任务会被放入线程池的任务队列中等待执行。由于循环是无限的,任务会不断地被提交,导致任务队列不断增大。内存消耗:随着任务队列中的任务越来越多,系统的内存消耗也会不断增加。最终,可能会导致内存耗尽,抛出 OutOfMemoryError 异常。线程池饱和:线程池中的 10 个线程会不断地从任务队列中取任务执行,但由于每个任务都要休眠 10 秒钟,任务处理的速度远远跟不上任务提交的速度,导致任务队列越来越长。

总结

最后给出常见OOM异常

java.lang.OutOfMemoryError: Java heap space

java.lang.OutOfMemoryError: unable to create new native thread

java.lang.OutOfMemoryError: Metaspace

java.lang.OutOfMemoryError: Direct buffer memory

java.lang.OutOfMemoryError: GC overhead limit exceeded

推荐阅读:

以上图片部分来自于下面链接,如有侵权,请联系本人

常见java OOM异常分析(原因+排查解决思路+demo代码)_java oom异常的原因分析-CSDN博客

使用JProfiler分析OOM系统快照_jprofiler如何打开快照-CSDN博客



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。