Java 虚拟机(JVM)发生 OutOfMemoryError(OOM)异常时,表示 JVM 在尝试分配内存时无法找到足够的内存资源。以下是几种常见的导致 OOM 异常的情况:
这种情况发生在 JVM 堆内存耗尽,无法再为新的对象分配空间。
1.查找关键报错信息,比如java.lang.OutOfMemoryError: Java heap space
2.使用内存映像分析工具(如Jprofiler)对Dump出来的堆储存快照进行分析,分析清楚是内存泄漏还是内存溢出。
这里给出我安装整合idea参考的教程
3.如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,修复应用程序中的内存泄漏。
4.如果不存在泄漏,先检查代码是否有死循环,递归等,再考虑用 -Xmx 增加堆大小。demo代码:java
代码解读复制代码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.查找报错关键信息
arduino
代码解读复制代码Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
2.使用内存映像分析工具Jprofiler分析产生的堆储存快照
(1)我们可以先通过 top -c查看当前服务器进程并记录当前消耗cpu最高线程的pid。
比如发现当前线程pid为744的使用率最高。
(2)然后通过下面的命令到处jvm内存快照ini
代码解读复制代码jmap -dump:formart=b.file=java_pid_744.hprof 744
(java_pid_744.hprof是文件名。 744是通过top c查看消耗cpu使用率最高的线程id)
然后下载到本地,下载先可以先压缩一下,这样可以节省时间。一个小技巧。
(3)使用上面下载好的JProfiler打开生成的单个快照
OOMObject这个类创建了11956010个实例,是属于内存溢出
然后点击这个最大对象分析
然后我这时候电脑卡着了,借用网图给接下来步骤说明
打开后右键打开使用选定对象
然后这里会显示详细的日志
这里可以看见具体的代码块。然后我们就可以定位代码结合具体代码进行分析。发现死循环了。
关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常:
查找关键报错信息,确定是StackOverflowError还是OutOfMemoryError如果是StackOverflowError,检查代码是否递归调用方法等如果是OutOfMemoryError,检查是否有死循环创建线程等,通过-Xss降低的每个线程栈大小的容量
代码解读复制代码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.排查代码,确定是否显示使用死循环创建线程
方法区,(又叫永久代,JDK8后,元空间替换了永久代),用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。运行时产生大量的类,会填满方法区,造成溢出。
使用CGLib生成了大量的代理类,导致方法区被撑爆在Java7之前,频繁的错误使用String.intern方法大量jsp和动态产生jsp应用长时间运行,没有重启
调整元空间大小(增加 -XX:MaxMetaspaceSize 参数)检查代码是否频繁错误得使用String.intern方法优化类加载机制,减少不必要的类加载,检查是否使用CGLib生成了大量的代理类重重启JVM
这种情况发生在本机内存耗尽时。
比如:NIO程序中,使用ByteBuffer.allocteDirect(capability)分配的是直接内存,可能导致直接内存溢出。
ByteBuffer分配128MB直接内存,而JVM参数-XX:MaxDirectMemorySize=100M指定最大是100M,因此发生直接内存溢出。
这种情况发生在垃圾回收频繁且回收效果不明显时(超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。)。
代码解读复制代码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
}
});
}
}
}