Java内存溢出(OOM)原因分析与解决方法:堆溢出、内存泄露排查指南

前言

Java内存溢出(OOM)原因分析与解决方法:堆溢出、内存泄露排查指南

撸 Java 的同学,会在一定程度上碰到内存溢出(OOM)的情况。而导致 OOM 出现的原因是各不相同的,有着多种形式。

堆溢出

这种场景是最为常见的。报错信息为:java.lang.OutOfMemoryError: Java heap space。

原因

1、代码中可能存在大对象分配

可能会有内存泄露的情况,这会使得在经过多次垃圾回收(GC)之后,依然无法找到一块足够大的内存来容纳当前的对象。

解决方法

1、检查是否存在大对象的分配,最有可能的是大数组分配

使用 jmap 命令将堆内存进行 dump 操作,然后利用 mat 工具对其进行分析,以此来检查是否存在内存泄露的问题。

3、如果没有找到明显的内存泄露,使用 -Xmx 加大堆内存

还有一点容易被忽视,需要检查是否存在大量的自定义的 Finalizable 对象,这些对象有可能是框架内部提供的,要考虑它们存在的必要性。

永久代/元空间溢出

报错信息:

java.lang.OutOfMemoryError: PermGen spacejava.lang.OutOfMemoryError: Metaspace

原因

HotSot 虚拟机的永久代是对方法区的具体实现。它存放了被虚拟机加载的类信息,还存放了常量,同时也存放了静态变量,并且存放了 JIT 编译后的代码等。

JDK8 之后,永久代被元空间所替换。元空间使用的是本地内存。同时还有其他的一些细节发生了变化。

字符串常量由永久代转移到堆中和永久代相关的JVM参数已移除

可能原因有如下几种:

在 Java7 之前,经常会错误地使用 String.intern()方法。

2、运行期间生成了大量的代理类,导致方法区被撑爆,无法卸载

应用长时间运行,且没有重启 JVM 进程的情况通常发生在调试期间,就像下面 tomcat 官网的一个常见问题解答(FAQ)中所描述的那样:

?.一个常见的问题是“PermGen”内存泄漏。.它们之所以发生,是因为类加载器。(and the Class objects it loaded)只有满足一些要求,才能进行回收。()., and when you redeploy a new class loader is created,它加载了所有这些类的另一份副本。.. (*)this.

解决方法

因为该OOM原因比较简单,解决方法有如下几种:

1、检查是否永久代空间或者元空间设置的过小

2、检查代码中是否存在大量的反射操作

dump 之后,要检查是否存在大量由于反射生成的代理类,可通过 mat 进行检查。

4、放大招,重启JVM

我在这里分享一些内容,其中包含很多实用的知识,有关于 jvm、netty、spring、线程、spring cloud 等方面的详细讲解,还有详细的学习规划图以及面试题整理等。我觉得在面试这方面讲解得十分清晰。如果想要获取面试资料,只需点击这里领取!暗号:CSDN。

GC overhead limit exceeded

这个异常比较少见,其报错信息为:java.lang.OutOfMemoryError:GC overhead limit exceeded。

原因

JDK6 新增了这种错误类型,通常是因为堆太小所导致的。Sun 官方对其的定义为:当超过 98%的时间都用于做 GC,并且回收的堆内存不到 2%时,就会抛出此异常。

解决方法

对代码进行优化。

添加参数 -XX:-UseGCOverheadLimit 来禁用这个检查。实际上,这个参数无法解决内存问题,仅仅是将错误信息延后了,最终会出现 java.lang.OutOfMemoryError: Java heap space 这种情况。

dump 内存,接着检查是否有内存泄露的情况。若没有发现内存泄露,就将内存加大。

方法栈溢出

报错信息表明出现了“java.lang.OutOfMemoryError”错误,具体情况是无法创建新的本地线程。

原因

出现这种异常,通常是因为创建了大量的线程。之前曾碰到过一次类似情况,通过 jstack 查看,一共出现了 8000 多个线程。

解决方法

1、通过 -Xss 降低的每个线程栈大小的容量

线程总数会受到系统空闲内存以及操作系统的限制。需要检查在该系统下是否存在这样的限制。

/proc/sys/kernel/pid_max

/proc/sys/kernel/thread-max 是最大用户进程数(ulimit -u)。

/proc/sys/vm/maxmapcount

非常规溢出

这些 OOM 异常,可能大部分同学未曾碰到过,不过依然需要对其有所了解。

分配超大数组

报错信息表明出现了“java.lang.OutOfMemoryError”的情况,具体是因为请求的数组大小超出了虚拟机的限制。

这种情况通常是由不合理的数组分配请求所引起的。在给数组分配内存之前,JVM 会进行一项检查。即要分配的数组在该平台上是否能够被寻址,如果不能被寻址,就会抛出这个错误。

解决方法就是检查你的代码中是否有创建超大数组的地方。

swap溢出

报错信息表明出现了“java.lang.OutOfMemoryError”,具体原因是“Out of swap space”,即超出了交换空间。

这种情况一般是操作系统导致的,可能的原因有:

1、swap 分区大小分配不足;

2、其他进程消耗了所有的内存。

解决方案:

也可以加大机器的内存大小。

本地方法溢出

报错信息为:java.lang.OutOfMemoryError ,具体内容为 stack_trace_with_native_method 。

本地方法在运行期间发生了内存分配失败。之前的方法是栈溢出,两者不同。方法栈溢出出现在 JVM 代码层面,而本地方法溢出则出现在 JNI 代码或本地方法处。

这个异常出现的概率非常低。只能借助操作系统的本地工具来进行诊断,而且难度比较大。所以还是放弃比较好。

最后

我在这里分享一个,有很多干货,包含jvm,netty,spring,线程,spring cloud等详细讲解,也有详细的学习规划图,面试题整理等,我感觉在面试这块讲的非常清楚:获取面试资料只需:点击这里领取!!! 暗号:CSDN

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

26阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

28阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

25阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

25阅读