java oom分析_Java OOM 分析

本文介绍了 Java 中 OOM 的概念,它指应用内存用完,涉及 JVM 管理的内存模型。阐述了 OOM 发生的原因,分为内存不足和代码问题。还列举了常见的 OOM 类型,最后给出线上环境遇到 OOM 时分析定位问题的思路,包括寻找日志和文件、查看内存使用情况及参数配置等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是 OOM

在 Java 中,OOM 是 java.lang.OutOfMemoryError 异常的缩写,简单来说是应用的内存用完了。

而这个内存,指代的是 JVM 管理的内存模型。

JVM 内存模型

JVM 在运行时管理的内存区域分别如下程序计数器。其作用是记录每个线程当前执行的字节码指令的位置,因为可能有多个线程并发执行不同的方法,所以程序计数器是每个线程私有的。

Java 虚拟机栈。Java 中方法的调用会为方法创建栈帧,然后在线程的 Java 虚拟机栈中进行出栈入栈,记录调用情况;除此,Java 虚拟机栈还会记录局部变量表,操作数栈等信息,同样为每个线程私有。

Java 堆内存。程序中创建的对象,便存储在 Java 堆内存中,其也是 GC 的主要地方,堆内存中的对象又按不同的 “生存” 情况分为新生代,老年代等。Java 堆内存为所有线程共有。

方法区 / Metaspace。该区域主要存放类的元信息,在 JDK 1.8 后新独立出来 Metaspace。

本地方法栈。为 natice 方法,调用本地操作系统类库提供的内存环境。

堆外内存。这一区域不属于 JVM 内存,是可直接访问的内存,如 NIO 会访问到这部分。

为什么会 OOM

发生 OOM,简单来说可总结为两个原因:分配给 JVM 的内存确实不够用

分配的内存是够用的,但是代码写的不好,多余的内存没有释放,最终导致内存不够用

OOM 类型

一般 OOM 分为三种类型java.lang.OutOfMemoryError: Java heap space。Java 堆内存溢出,此种情况最常见。

java.lang.OutOfMemoryError: PermGen space。Java 永久代溢出,即方法区溢出了。

java.lang.StackOverflowError。不会抛 OOM ERROR,但也是比较常见的 Java 内存溢出。

分析 OOM 思路

接下来简单总结,在线上环境遇到 OOM 时,分析定位问题的思路。寻找 GC 日志和 dump 文件。 这里有个问题,就是不知道线上应用有没有开启打印 GC 日志和 dump 文件,这里寻找的思路如下

// 1.查找对应的应用的 pid

jps -v // 输出所有 Java 应用,找对应 pid,或使用 ps -ef | grep xxx 来寻找

// 2.查看该 pid 启动应用时的 VM 参数

jinfo -flags pid

获取到的 VM 参数,可关注如下几项参数的情况

-XX:+HeapDumpOnOutOfMemoryError // 是否开启 OOM 时输出 dump 文件,+ 表示开启

-XX:HeapDumpPath=/data/logs // 若配置该项,dump 文件输出到该路径下

-XX:+PrintGCDetails // 是否打印 GC 详细信息

-XX:+PrintGCTimeStamps // 是否打印 GC 时间戳(基准形式)

-XX:+PrintGCDateStamps // 是否打印 GC 时间戳(日期形式)

-XX:+PrintTenuringDistribution // 在每次新生代 GC 时,输出幸存区中对象的年龄分布

-Xloggc:/data/logs // 若配置该项,GC 日志输出到该路径下若应用没有启用相应的参数输出日志,这里简单查看应用 JVM 内存使用情况的思路如下

// 1.查看应用 GC 次数及平均每次 GC 时间

jstat -gc pid 5000 // 每 5 秒显示一次 pid 的进程生成 GC 情况

// 2.查看 JVM 堆内存不同代的具体使用情况

jmap -heap pid同时,应该在这时加上对应的参数,见上面 [获取到的 VM 参数] 示例。

那么如何加上参数呢?

有几个方法可以设置。首先是加在 Tomcat 的启动文件上(/bin/catalina.sh),如下新增配置

JAVA_OPTS="-XX:+HeapDumpOnOutOfMemoryError ... "

因为我们是一个 Tomcat 只部署一个应用,这样配置在 Tomcat 启动文件上,其粒度便只针对当前应用。

除此之外,也可以配置在 Maven 启动文件上(%MAVEN_HOME%/bin/mvn),配置语法与上面类似。因为有多个应用使用同个 Maven,因此最后没有采用该种配置方法。

配置完参数后,重新发布应用,若下次有 OOM 情况出现,便可分析对应日志来定位具体问题。

脑图

参考文章

END

### Java OOM 错误原因及解决方法 Java 中的 OutOfMemoryError(简称 OOM)是常见的内存溢出错误,通常发生在 JVM 无法分配所需内存时。以下是详细的分析和解决方案: #### 1. **Java Heap Space** 当堆内存(Heap Space)没有足够空间存放新创建的对象时,会抛出 `java.lang.OutOfMemoryError: Java heap space` 错误。这种问题通常与程序中对象的生命周期管理不当有关[^3]。 - **解决方法**: - 增加堆内存大小,通过 JVM 参数 `-Xms` 和 `-Xmx` 设置初始堆大小和最大堆大小。 - 使用内存分析工具(如 VisualVM、JProfiler 或 Eclipse MAT)检查是否存在内存泄漏或大对象占用过多内存的情况。 - 确保对象在不再需要时被及时释放,避免不必要的引用。 ```bash java -Xms512m -Xmx4g MyApplication ``` #### 2. **PermGen/Metaspace** 在 JDK 8 之前,永久代(PermGen)用于存储类的元数据。如果加载了大量类且 PermGen 空间不足,会抛出 `java.lang.OutOfMemoryError: PermGen space` 错误。从 JDK 8 开始,永久代被 Metaspace 替代。 - **解决方法**: - 对于 JDK 8+,可以通过 `-XX:MaxMetaspaceSize` 参数调整 Metaspace 的大小。 - 减少动态生成类的数量,例如使用类加载器优化技术。 - 如果使用第三方框架(如 Spring、Hibernate),确保其版本兼容并优化类加载行为。 ```bash java -XX:MaxMetaspaceSize=256m MyApplication ``` #### 3. **Direct Memory** 直接内存(Direct Memory)由 JVM 外部管理,通常用于 NIO 操作。如果直接内存耗尽,会抛出 `java.lang.OutOfMemoryError: Direct buffer memory` 错误[^2]。 - **解决方法**: - 调整直接内存的最大值,通过 `-XX:MaxDirectMemorySize` 参数设置。 - 检查代码中是否正确释放了 ByteBuffer 等资源。 - 避免频繁创建和销毁大量的直接缓冲区。 ```bash java -XX:MaxDirectMemorySize=128m MyApplication ``` #### 4. **Thread Stack Size** 线程栈大小不足或线程过多时,会抛出 `java.lang.OutOfMemoryError: unable to create new native thread` 错误。这通常是由于系统资源限制或线程池配置不当引起的。 - **解决方法**: - 减少线程数量,优化线程池配置。 - 调整线程栈大小,通过 `-Xss` 参数设置。 - 检查操作系统对线程数的限制,并适当调整。 ```bash java -Xss256k MyApplication ``` #### 5. **GC Overhead Limit Exceeded** 当垃圾回收(GC)过于频繁且每次回收后仅能回收少量内存时,会抛出 `java.lang.OutOfMemoryError: GC overhead limit exceeded` 错误。这通常表明内存使用效率低下。 - **解决方法**: - 增加堆内存大小以减少 GC 频率。 - 优化代码,减少临时对象的创建。 - 调整 GC 参数,选择适合应用的垃圾回收器(如 G1、CMS)。 ```bash java -XX:+UseG1GC MyApplication ``` #### 6. **Native Memory** 本地内存(Native Memory)由 JVM 外部管理,通常用于 JNI 调用或内部数据结构。如果本地内存耗尽,会抛出 `java.lang.OutOfMemoryError: native memory exhausted` 错误[^2]。 - **解决方法**: - 检查 JNI 代码中是否存在内存泄漏。 - 优化第三方库的使用,避免不必要的内存分配。 - 监控和分析 JVM 的本地内存使用情况。 --- ### 综合案例分析 假设一个 Web 应用在高并发场景下频繁出现 OOM 错误,可能的原因包括堆内存不足、线程过多或直接内存泄漏。通过以下步骤可以定位问题: 1. 分析堆转储文件(Heap Dump),识别内存泄漏的根源。 2. 检查线程池配置,确保线程数量合理。 3. 使用监控工具(如 JConsole 或 Prometheus)观察 JVM 内存使用趋势。 --- ### OOM 的预防与调优建议 - 定期进行性能测试和压力测试,发现潜在的内存问题。 - 使用内存分析工具定期检查内存使用情况。 - 为关键业务配置告警机制,及时发现和处理 OOM 问题。 - 根据应用特点选择合适的 JVM 参数和垃圾回收器。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值