线上JVM OOM问题,如何排查和解决?

article/2025/7/16 21:03:15

今天咱们来聊聊让无数 Java 开发者头疼的 JVM OOM(Out Of Memory,内存溢出)问题。在面试中,OOM 问题也是面试官的“心头好”,因为它能直接考察你对 JVM 的理解,以及你在实际问题面前的排查和解决能力。

一、JVM OOM 到底是什么?

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

二、OOM 为啥会发生?

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

  1. 内存分配不足:JVM 初始化时,堆内存、永久代(或元空间)等区域分配得太小,根本不够业务跑。比如,你的应用要处理海量数据,但堆内存只给了 128MB,这不就是“杯水车薪”嘛。
  2. 大对象申请:一次性申请的内存太大,超出了 JVM 的承受范围。比如,你试图一次性加载一个几 GB 的文件到内存中,JVM 根本就装不下。
  3. 内存泄漏:程序中某些地方申请了内存,但因为代码逻辑错误,这些内存永远不会被释放,就像一个无底洞,不断吞噬着 JVM 的内存。
  4. 代码问题:程序里某些对象被频繁创建,用完后却没有被及时释放,导致内存被一点点蚕食。比如,一个定时任务不断往缓存里塞数据,但从来没清理过,时间一长,内存就被塞满了。

三、OOM 都有哪些“变种”?

1. Java 堆内存溢出

这是 OOM 最常见的形式,错误信息是 java.lang.OutOfMemoryError: Java heap space。堆内存是 JVM 里存放对象实例的地方,如果堆内存满了,垃圾回收器又没办法清理出足够的空间,就会触发这个错误。

2. 永久代/元空间溢出

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

3. 栈内存溢出

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

4. 直接内存溢出

直接内存是 JVM 外的一块内存,通常用于 NIO 操作。如果程序中大量使用 NIO,且没有正确管理直接内存,就会导致直接内存溢出,抛出 java.lang.OutOfMemoryError: Direct buffer memory 错误。

四、排查 OOM 的“杀手锏”

当线上服务出现 OOM 时,别慌,我们有这些“杀手锏”:

1. 启用 JVM 诊断选项

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

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dump
-Xlog:gc* (JVM 9 及以上)
-XX:+PrintGCDetails -Xloggc:/path/to/gc.log (JVM 8 及以下)

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

2. 分析错误日志

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

3. 分析堆转储文件

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

4. 检查 GC 日志

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

5. 代码审查和优化

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

五、解决 OOM 的“锦囊妙计”

1. 增加内存

  • 堆内存:用 -Xmx 参数增加最大堆内存,比如 -Xmx2g
  • 永久代/元空间:用 -XX:MaxPermSize(JDK 7 及以下)或 -XX:MaxMetaspaceSize(JDK 8 及以上)增加大小。
  • 直接内存:用 -XX:MaxDirectMemorySize 参数增加直接内存大小。

2. 优化代码

  • 释放对象:确保用完的对象能被垃圾回收,比如把不用的缓存清掉。
  • 避免大对象:能不用大对象就不用,实在要用,也尽量拆分成小块。
  • 用弱引用/软引用:比如缓存可以用 WeakHashMapSoftReference,避免内存泄漏。

3. 调优垃圾回收器

根据应用的特点,选择合适的 GC 算法(比如 G1、CMS),并调整参数,比如 -XX:+UseG1GC -XX:MaxGCPauseMillis=200

4. 管理外部资源

确保文件句柄、数据库连接等外部资源用完后能正确关闭,别让它们占着内存不放。

5. 持续监控和预警

用 JMX、Prometheus、Grafana 等工具实时监控 JVM 内存使用情况,一旦发现异常,立刻报警,提前解决问题。

六、实战案例分析

案例一:大数据量处理导致堆内存不足

症状:应用处理大数据量时,抛出 java.lang.OutOfMemoryError: Java heap space

排查

  • 启用 GC 日志和堆转储选项。
  • 分析 GC 日志,发现 Full GC 频繁,但内存还是不够用。
  • 用 JVisualVM 分析堆转储文件,发现大量大对象占用了内存。

解决

  • 优化算法,减少内存占用。
  • 通过 -Xmx 增加堆内存。
  • 改进数据处理流程,比如用流式处理,减少内存峰值。

案例二:动态类生成导致元空间不足

症状:动态生成类时,抛出 java.lang.OutOfMemoryError: Metaspace

排查

  • 启用堆转储和 GC 日志选项。
  • 分析 GC 日志,发现元空间增长飞快,类加载频繁。
  • 用工具查看元空间内容,发现大量动态生成的类没被卸载。

解决

  • 通过 -XX:MaxMetaspaceSize 增加元空间大小。
  • 优化动态类生成逻辑,减少不必要的类加载。

案例三:递归调用过深导致栈内存不足

症状:递归调用抛出 java.lang.StackOverflowError

排查:分析错误堆栈,发现递归调用深度太大。

解决

  • 改用迭代算法替代递归。
  • 优化算法,减少递归深度。

七、总结

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

希望这篇文章能帮助你在面试中更好地回答 OOM 相关问题,也能在实际工作中解决类似问题。如果你在工作中也遇到过 OOM 问题,欢迎在评论区留言,我们一起交流经验。


最后再分享一道常见的后端面试题。

说说main方法的执行过程?

示例代码:

public class Application {public static void main(String[] args) {Person p = new Person("大彬");p.getName();}
}class Person {public String name;public Person(String name) {this.name = name;}public String getName() {return this.name;}
}

执行main方法的过程如下:

  1. 编译Application.java后得到 Application.class 后,执行这个class文件,系统会启动一个 JVM 进程,从类路径中找到一个名为 Application.class 的二进制文件,将 Application 类信息加载到运行时数据区的方法区内,这个过程叫做类的加载。
  2. JVM 找到 Application 的主程序入口,执行main方法。
  3. main方法的第一条语句为 Person p = new Person("大彬") ,就是让 JVM 创建一个Person对象,但是这个时候方法区中是没有 Person 类的信息的,所以 JVM 马上加载 Person 类,把 Person 类的信息放到方法区中。
  4. 加载完 Person 类后,JVM 在堆中分配内存给 Person 对象,然后调用构造函数初始化 Person 对象,这个 Person 对象持有指向方法区中的 Person 类的类型信息的引用。
  5. 执行p.getName()时,JVM 根据 p 的引用找到 p 所指向的对象,然后根据此对象持有的引用定位到方法区中 Person 类的类型信息的方法表,获得 getName() 的字节码地址。
  6. 执行getName()方法。

最后分享一份大彬精心整理的大厂面试手册,包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~

需要的小伙伴可以自行下载

http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd

围观朋友⭕:dabinjava


http://www.hkcw.cn/article/ujYkaeasjj.shtml

相关文章

JVM常见线上问题:CPU 100%、内存泄露问题排查

一、CPU 100% 问题排查 1.1、找到 cpu 占有率最高的 java 进程号 使用命令: top -c 显示运行中的进程列表信息, shift + p 使列表按 cpu 使用率排序显示。 PID = 2227 的进程,cpu 使用率最高 1.2、根据进程号找到 cpu 占有率最高的线程号 使用命令: top -Hp {pid} ,同…

JVM 一文详解

目录 JVM 简介 JVM 中的内存区域划分 1. 堆(一个进程只有一份 ------ 线程共享) 2. 栈(一个进程可以有 N 份 ------ 线程私有) Java 虚拟机栈: 本机方法栈: 3. 程序计数器(一个线程可以…

【JVM】关于JVM的内部原理你到底了解多少(八股文面经知识点)

前言 🌟🌟本期讲解关于HTTPS的重要的加密原理~~~ 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 🔥 你的点赞就是小编不断更新的最大动力 🎆那么废话不…

深入理解 JVM 的栈帧结构

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s…

JVM 机制

目录 一、什么是 JVM: 二、JVM 的运行流程: 三、JVM 内存区域划分: 1、( 1 ) 程序计数器: 1、( 2 ) 元数据区: 1、( 3 ) 栈: 1、( 4 ) 堆: 四、类加载: 1、什么时候会触…

【JVM】类加载机制

文章目录 类加载机制类加载过程1. 加载2. 验证3. 准备4. 解析偏移量符号引用和直接引用 5. 初始化 类加载机制 类加载指的是,Java 进程运行的时候,需要把 .class 文件从硬盘读取到内存,并进行一些列的校验解析的过程(程序要想执行…

【JVM】从零开始深度解析JVM

本篇博客给大家带来的是JVM的知识点, 重点在类加载和垃圾回收机制上. 🐎文章专栏: JavaEE初阶 🚀若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅🚀 …

一篇文章带你解决笔试面试中的jvm问题

JVM内存区域划分 JVM启动的时候,会申请到一整个很大的内存区域.JVM是一个应用程序,要从操作系统里申请内存.JVM就根据需要,把空间分为几个部分,每个部分各自有不同的功能.具体划分如下: 分为:栈,堆,程序计数器,元数据区 Heap(堆):…

通义灵码2.5——基于编程智能体开发Wiki多功能搜索引擎

引言 在智能化浪潮重塑软件开发范式的今天,我借助开发一个基于编程智能体开发Wiki 多功能搜索引擎,深度体验了通义灵码2.5这一阿里云旗舰级AI编码助手,构建智能协作新范式。 该平台通过三大技术突破赋能开发全流程:基于编程智能…

基于SpringBoot的商家销售管理网站的设计与实现

湖南软件职业技术大学 本科毕业设计(论文) 设计(论文)题目 基于SpringBoot的商家销售管理网站的设计与实现 学生姓名 学生学号 所在学院 专业班级 校内指导教师 企业指导教师 毕业设计(论文)真实性承诺及声明 学生对毕业设计(论文)真实性承诺 本人郑重声明:所提交的毕…

各种噪声电流激励下电源PDN网络对系统时钟性能的影响

点击上面“蓝字”关注我们 电源分配是支持所有类型硅产品运行的基础设施的重要组成部分,但在设计过程中常被忽视。电源质量可能限制电路性能,并决定其工作可靠性。要真正解决电源分配问题,必须考虑包括芯片、封装和PCB在内的整个系统。 芯片…

【大模型】情绪对话模型项目研发

一、使用框架: Qwen大模型后端Open-webui前端实现使用LLamaFactory的STF微调数据集,vllm后端部署, 二、框架安装 下载千问大模型 安装魔塔社区库文件 pip install modelscope Download.py 内容 from modelscope import snapshot_downlo…

关于位图Bitmaps的介绍

目录 1、基本概念 1.1、介绍 1.2、关键字 1.3、结构原理 2、常用命令 2.1、SETBIT 2.2、GETBIT 2.3、BITCOUNT 2.4、BITOP 2.5、BITPOS 3、应用场景 4、使用示例 前言 Redis的Bitmaps是一种基于字符串的数据结构,用于处理位级别的操作。 Bitmaps在Redis…

【软件设计】通过软件设计提高 Flash 的擦写次数

目录 0. 个人简介 && 授权须知1. Flash 和 EEROM 基本情况2. 场景要求3. 软件设计思路4. 代码展示4.1 flash.h4.2 flash.c 0. 个人简介 && 授权须知 📋 个人简介 💖 作者简介:大家好,我是喜欢记录零碎知识点的菜鸟…

C 语言练习--初级

#学习C 代码, 做小练习时,自己运行代码竟然发现很多错误,记录一下。 1、计算器 根据输入的数值和符合,输出相应结果。 结果: #include "stdio.h"int Primary_math(int a, int b, char sign){int num0;swit…

pikachu通关教程-CSRF XSS

XSS XSS漏洞原理 XSS被称为跨站脚本攻击(Cross Site Scripting),由于和层叠样式表(Cascading Style Sheets,CSS)重名,改为XSS。主要基于JavaScript语言进行恶意攻击,因为js非常灵活…

E. Melody 【CF1026 (Div. 2)】 (求欧拉路径之Hierholzer算法)

E. Melody 思路 将所有出现过的音量和音高看作一个点,一个声音看作一条边,连接起来。那么很容易知道要找的就是图上的一条欧拉路径(类似一笔画问题) 又已知存在欧拉路径的充要条件为:度数为奇数的点的个数为0或者2个…

历年中国科学技术大学计算机保研上机真题

2025中国科学技术大学计算机保研上机真题 2024中国科学技术大学计算机保研上机真题 2023中国科学技术大学计算机保研上机真题 在线测评链接:https://pgcode.cn/school?classification1 拆分数字 题目描述 给定一个数字,拆分成若干个数字之和&#xff…

2025陕西省赛补题

A 贪心 题意:给一个长度为n的序列,每次操作可以花费 w [ c [ i ] ] ( r − l 1 ) w[c[i]](r-l1) w[c[i]](r−l1)的代价,把区间 [ l , r ] [l,r] [l,r]染成染色 。 思路:对任意颜色,[l,r]中如果有cnt个连续的该颜色段…

Linux详谈进程地址空间

目录 第一谈:简单了解 第二谈:与操作系统的联系 内核空间与用户空间 步骤1:用户态代码执行 步骤2:跳转到内核代码 步骤3:内核代码访问用户数据 步骤4:返回到用户态 对于操作系统的本质:…