当前位置: 首页 > web >正文

JVM OOM问题排查与解决思路

今天咱们来聊聊让无数 Java 开发者头疼的 JVM OOM(Out Of Memory,内存溢出)问题。

目录

一、JVM OOM 到底是什么?

二、OOM 为啥会发生?

三、OOM 都有哪些类型呢?

1.堆内存溢出

2.永久代/原空间溢出

3.栈内存溢出

4.直接内存溢出

四、排查 OOM 的“杀手锏”

通过MAT作为示例定位问题

0.获取dump文件

1.打开Heap Dump文件:启动MAT并打开Heap Dump文件(.hprof)

2.运行Leak Suspects Report :MAT可自动生成一个内存泄漏(Leak Suspects Report),该报告指出可能内存泄漏路径

3.分析Dominators Tree:该视图显示了占用最多的内存对象及其引用。通过1它,可找到最大的内存消耗者。

4.查看Histogram:列出所有对象的实例数和总大小,帮助识别那种类型的对象占用最多的内存

5.检查GC Roots:确定对象为什么没有被垃圾回收,可查看对象到GC Roots的引用链

6.分析引用链:通过分析对象的引用链,可确定是什么持有了这些对象的引用,导致它们无法被回收

通过jvisualvm定位OOM问题

通过Arthas工具定位问题

五、总结


一、JVM OOM 到底是什么?

简单来说JVM OOM就是Java虚拟机的内存用完了,而且垃圾回收器(GC)也无能为力,没办法再为新对象分配内存,于是抛出了错误。这就好比你开着一辆车,邮箱里的油已经耗尽,但你还想继续加速,结果只能熄火。

二、OOM 为啥会发生?

OOM的原因多种多样,但归根结底就两个字————“不够用”。具体来说,有这么几种常见情况:

1.内存分配不足:JVM初始化时。堆内存、永久代(或元空间)等区域分配得太小,根本不够业务跑。     比如,你的应用要处理海量数据,但堆内存只给了128MB,这不就是“杯水车薪”嘛。这种我们称不上bug,只是亦或是称为 “巧妇难为无米之炊”吧

2.大对象申请:一次性申请的内存太大,超出了JVM的承受范围。     比如,试图一次性加载一个几GB的文件到内存中,JVM根本就装不下。

3.内存泄漏:程序中某些地方申请了内存,但是因为代码逻辑错误,这些已经无用的内存却一直被引用,得不到释放,就像一个无底洞,不断吞噬JVM的内存。

4.代码问题:程序里某些对象被频繁创建,用完却没有被及时释放,导致内存被一点点占用,比如,一个定时任务不断往缓存中赛数据,但从来没清理过,时间一长,内存就被塞满了。

三、OOM 都有哪些类型呢?

1.堆内存溢出

这是OOM最常见的形式

错误信息是:

java.lang.OutOfMemoryError: Java heap spac

堆内存是JVM里存放对象实例的地方,如果堆内存满了,垃圾回收器又没办法清理足够的空间,就会触发这个错误。

2.永久代/原空间溢出

报错信息:

java.lang.OutOfMemoryError: PermGen space
java.lang.OutOfMemoryError: Metaspace

在JDK7及以下版本中,永久代(PermGen)用于存放类的元数据、常量池等信息。如果应用加载了大量类,比如使用了动态代理、字节码操作等技术,永久代很容易被撑爆,抛出错误。从JDK 8 开始,永久代被原空间(Metaspace)取代,但原理类似。

核心原因:生成大量动态类

 1.使用大量动态生成类的框架,如ORM框架,动态代理技术、热部署工具等

 2.程序代码中大量使用,反射在大量使用时,由于使用缓存的原因,会导致ClassLoader和他引用的Class等对象不能被回收。

3.栈内存溢出

报错信息:

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

栈内存是线程私有的,用于存放方法调用的局部变量、操作数栈等信息。如果一个方法调用链太深,比如递归调用过深,或者方法里的局部变量过多,栈内存就会溢出,虽然名字里面又“Overflow”,但本质也是OOM的一种。

4.直接内存溢出

报错信息:

java.lang.OutOfMemoryError: Direct buffer memory

Java堆外内存OOM值的是Java直接使用非堆内存耗尽导致的,这部分内存主要用于Java NIO库,允许Java程序以更接近操作系统的方式管理内存,常用于高性能缓存、大型数据处理等场景。

四、排查 OOM 的“杀手锏”

1.启动JVM诊断选项

在启动应用时,加上这些参数

-XX:+HeapDumpOnOutOfMemoryError       //  指示JVM在遇到OOM错误时生成堆转储文件

-XX:HeapDumpPath=/path/to.dump            //指定堆转储文件的存储路径,可以自定义路径和文件名

-Xlog:gc*(JVM 9及以上)

-XX:PrintGCDeails -Xloggc:/path/to/gc.log(JVM8及以下)

这些参数可以让JVM在OOM的时生成内存堆转储文件和GC日志,帮助我们分析问题。

2. 分析错误日志

仔细查看应用日志和OOM错误堆栈信息,看看哪个内存区域出了问题。

3.分析堆转储文件

用JVisualIVM、MAT这些工具打开堆转储文件,找出内存占用大户,看看是不是有点内存泄漏。

当应用抛出OOM并且根据上述设置生成了堆转储文件后,使用Heap Dump分析工具来分析文件,常用的工具有:

 Eclipse Memory Analyzer(MAT):    一个强大的]ava堆分析工具,可以帮助识别内存泄漏和査看内存消耗等情况。


VisuaIVM:  除了监控功能外,也支持加载和分析Head Dump文件。

4.检查GC日志

分析GC日志,看看垃圾回收的概率、暂时时间和各内存区的使用情况,判断是不是垃圾回收出了问题。

5.代码审查和优化

从代码层找原因,看看是不是有缓存没清理、静态集合不断增长等内存泄漏问题。发现问题后,优化代码,减少对象创建,及时释放内存。

通过MAT作为示例定位问题

0.获取dump文件

注意导出文件占用内存很大的时候,可能会导不出来

方式一:提前配置JVM参数,在系统挂了后会在指定目录下生成dump文件

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=./ (导出路径)

方式二:使用命令行导出

jmap -dump:format=b,file=demo.hprof <pid>

1.打开Heap Dump文件:启动MAT并打开Heap Dump文件(.hprof)

2.运行Leak Suspects Report :MAT可自动生成一个内存泄漏(Leak Suspects Report),该报告指出可能内存泄漏路径

3.分析Dominators Tree:该视图显示了占用最多的内存对象及其引用。通过1它,可找到最大的内存消耗者。

4.查看Histogram:列出所有对象的实例数和总大小,帮助识别那种类型的对象占用最多的内存

5.检查GC Roots:确定对象为什么没有被垃圾回收,可查看对象到GC Roots的引用链

6.分析引用链:通过分析对象的引用链,可确定是什么持有了这些对象的引用,导致它们无法被回收

ps:授人以鱼不如授人以渔,en!有那味啦

MAT下载教程:

mac pro m1:安装dump文件内存分析工具——MAT_dump文件分析工具-CSDN博客

MAT使用教程~:线上又 OOM 了 ,教你快速定位问题~-腾讯云开发者社区-腾讯云

通过jvisualvm定位OOM问题

 通过jvisualvm工具装载dump文件

查看内存占用最高的业务对象,并找到GCRoot并查看线程栈从定位问题

通过Arthas工具定位问题

执行如下命令下载arthas-boot.jar,再用java -jar命令启动:

wgethttps://arthas.aliyun.com/arthas-boot.jar; java-jararthas-boot.jar

arthas-boot是Arthas的启动程序,它启动后,会列出所有的Java进程,用户可以选择需要诊断的目标进程。

使用dashboard 命令可以查看当前系统的实时数据面板。可以查看到CPU、内存、GC、运行环境等信息。

使用 sc 命令来查找JVM里已加载的类,通过-d参数,可以打印出类加载的具体信息,很方便查找类加载问题。

五、总结

JVM OOM 是一个复杂但常见的问题,它可能出现在堆内存、永久代/元空间、栈内存或直接内存等区域。排查 OOM 的关键在于启用诊断选项(如堆转储和 GC 日志)、分析错误日志和堆转储文件、检查垃圾回收日志。解决 OOM 的方法包括增加内存、优化代码、调优垃圾回收器参数和管理外部资源。持续监控和预警机制可以有效预防 OOM 问题的发生。

http://www.xdnf.cn/news/18793.html

相关文章:

  • Flask蓝图:模块化开发的利器
  • HarmonyOS NEXT系列之元服务框架ASCF
  • 第04章 SPSS简介与数据库构建
  • 【机器学习】9 Generalized linear models and the exponential family
  • BQTLOCK 勒索软件即服务出现,拥有复杂的规避策略
  • 大白话解析:多证明验证(Merkle Multi-Proof)​
  • 可视化-模块1-HTML-03
  • 基于SpringBoot的美食分享平台【2026最新】
  • 构建wezzer平台!
  • Indy HTTP Server 使用 OpenSSL 3.0
  • 知识蒸馏 Knowledge Distillation 1. 监督式微调(SFT):极大似然是前向 KL 的特例
  • Grafana k6 性能测试
  • 深度模块化剖析:构建一个健壮的、支持动态Cookie和代理的Python网络爬虫
  • 保姆级Maven安装与配置教程(Windows版)
  • 基于 MATLAB 的信号处理实战:滤波、傅里叶变换与频谱分析
  • 从文本树到结构化路径:解析有限元项目架构的自动化之道
  • 服务器硬件电路设计之 SPI 问答(四):3 线 SPI、Dual SPI 与 Qual SPI 的奥秘
  • Leetcode 3660. Jump Game IX
  • k8sday16调度器
  • MSF基础知识
  • Java 内存模型(JMM)与并发可见性:深入理解多线程编程的基石
  • Java:HashMap的使用
  • K8s 实战:六大核心控制器
  • 什么是 Nonce?
  • 电力电子simulink练习10:反激Flyback电路搭建
  • Linux 的 TCP 网络编程常用API
  • 图像均衡化详解:从直方图均衡到 CLAHE,让图片告别 “灰蒙蒙“
  • 高数 不定积分(4-3):分部积分法
  • 使用虚幻引擎5(UE5)开发类似《原神》的开放世界游戏:从技术架构到实践指南
  • 内网后渗透攻击--域控制器安全(1)