美团面试:说说OOM三大场景和解决方案? (绝对史上最全)
首先,咱们先聊聊,什么是OOM?
小伙伴们,有没有遇到过程序突然崩溃,然后抛出一个OutOfMemoryError的异常?这就是我们俗称的OOM,也就是内存溢出。简单来说,就是你的Java应用想要的内存超过了JVM愿意给的极限,就会抛出这个错误。
那么为什么会出现OOM呢?一般都是由这些问题引起:
分配过少:JVM 初始化内存小,业务使用了大量内存;或者不同 JVM 区域分配内存不合理
内存泄漏:某一个对象被频繁申请,不用了之后却没有被释放,发生内存泄漏,导致内存耗尽(比如ThreadLocal泄露)
接下来,我们来聊聊Java OOM的三大经典场景以及解决方案,保证让你有所收获!
Java OOM的三大核心场景

场景一:堆内存OOM(也叫堆内存溢出)
这是最常见的OOM场景了,发生在JVM试图分配对象空间时,却发现剩余的堆内存不足以存储新对象。
例如我们执行下面的代码,就可以模拟出堆内存OOM的场景:
// 创建大量对象导致堆内存溢出
public class HeapOOM {
static class OOMObject {
// 假设这里有一些属性
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while (true) {
list.add(new OOMObject()); // 不断创建对象并添加到list中
}
}
}
那么当出现线上应用OOM场景时,该如何解决呢?

分析方法通常有两种:
类型一:在线分析,属于轻量级的分析:
类型二:离线分析,属于重量级的分析:
类型一:在线OOM分析:
在线分析Java OOM(内存溢出)问题,通常涉及到监控运行中的Java应用,捕获内存溢出时的信息,分析堆转储(Heap Dump)文件,以及利用一些工具和命令来辅助定位问题。下面是一套详细的分析流程和命令,帮助你在线分析和解决Java OOM问题:
1、启用JVM参数以捕获Heap Dump
在Java应用启动命令中加入以下JVM参数,以确保在发生OOM时能自动生成堆转储文件:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/heapdump.hprof
这些参数的作用是:
-XX:+HeapDumpOnOutOfMemoryError:指示JVM在遇到OOM错误时生成堆转储文件。-XX:HeapDumpPath:指定堆转储文件的存储路径,可以自定义路径和文件名。
2、实时监控内存使用情况
使用jvisualvm或jconsole等工具可以实时监控Java应用的内存使用情况。这些工具可以帮助你了解内存消耗的趋势,从而预测和避免OOM的发生。
- JVisualVM:集成了多个JDK命令行工具,提供了可视化界面,可以监控内存使用、查看线程、分析堆等。
- JConsole:Java监控和管理控制台,用于对JVM中的内存、线程和类等进行监控。
3、分析Heap Dump文件
当应用抛出OOM并且根据上述设置生成了堆转储文件后,使用Heap Dump分析工具来分析这个文件。常用的工具有:
- Eclipse Memory Analyzer (MAT):一个强大的Java堆分析工具,可以帮助识别内存泄露和查看内存消耗情况。
- VisualVM:除了监控功能外,也支持加载和分析Heap Dump文件。
在MAT中打开Heap Dump文件,主要关注以下几点:
- 查找内存中对象的分布,特别是占用内存最多的对象。
- 分析这些对象的引用链,确定是哪部分代码引起的内存泄漏或过度消耗。
- 检查ClassLoader,以确认是否有过多的类被加载导致的元空间(Metaspace)OOM。
4、使用命令行工具
JDK提供了一些命令行工具,如jmap,可以用来生成Heap Dump文件:
jmap -dump:live,format=b,file=heapdump.hprof <pid>
其中<pid>是Java进程的ID。-dump:live选项表示只转储活动对象,可以减小Heap Dump文件的大小。
5、分析日志和异常信息
最后,不要忽视应用的日志和抛出的异常信息。OOM之前的日志可能会提供一些导致内存溢出的操作或业务逻辑的线索。
类型二:离线OOM分析,这个属于重量级分析
离线分析Java OOM(OutOfMemoryError)通常是在问题发生后,通过分析JVM生成的堆转储(Heap Dump)文件来进行。这个过程涉及到获取堆转储文件、使用分析工具进行深入分析和解读分析结果
1、获取Heap Dump文件
首先,确保你已经有了一个Heap Dump文件。这个文件可能是在JVM遇到OOM时自动生成的(如果启用了-XX:+HeapDumpOnOutOfMemoryError JVM参数),或者你可以在应用运行期间手动生成:
使用
jmap命令生成Heap Dump文件:jmap -dump:live,format=b,file=/path/to/heapdump.hprof <pid>
其中
<pid>是Java进程的ID,/path/to/heapdump.hprof是你希望保存Heap Dump文件的位置。
2、使用Heap Dump分析工具
有了Heap Dump文件后,你需要使用专门的工具来进行分析。以下是一些常用的分析工具:
- Eclipse Memory Analyzer (MAT):非常强大的内存分析工具,能帮助识别内存泄漏和查看内存消耗情况。
- VisualVM:提供了一个可视化界面,可以用来分析Heap Dump文件。
- JVisualVM:随JDK一起提供的工具,也支持加载Heap Dump文件进行分析。
3、分析Heap Dump文件
使用MAT(Eclipse Memory Analyzer)作为示例,分析流程如下:
打开Heap Dump文件:启动MAT并打开Heap Dump文件(.hprof)。
运行Leak Suspects Report:MAT可以自动生成一个内存泄漏报告(Leak Suspects Report),这个报告会指出可能的内存泄漏路径。
分析Dominators Tree:这个视图显示了占用最多内存的对象及其引用。通过它,你可以找到最大的内存消耗者。
查看Histogram:对象Histogram列出了所有对象的实例数和总大小,帮助你识别哪种类型的对象占用了最多的内存。
检查GC Roots:为了确定对象为什么没有被垃圾回收,可以查看对象到GC Roots的引用链。
分析引用链:通过分析对象的引用链,你可以确定是什么持有了这些对象的引用,导致它们无法被回收。
下面给大家提供一份Java应用上线前参考的的JVM配置(内存8G),以后系统上线前可以先配置下JVM,不要啥都不配置就上线了
-Xms6g -Xmx6g (按不同容器,4G及以下建议为50%,6G以上,建议设置为70%)
-Xmn2g (以8G内存,年轻代可以设置为2G)
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
-Xss256k
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:AutoBoxCacheMax=20000
-XX:+HeapDumpOnOutOfMemoryError (当JVM发生OOM时,自动生成DUMP文件)
-XX:HeapDumpPath=/usr/local/logs/gc/
-XX:ErrorFile=/usr/local/logs/gc/hs_err_%p.log (当JVM发生崩溃时,自动生成错误日志)
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/usr/local/heap-dump/
场景二:元空间(MetaSpace)OOM
什么是元空间?
Java元空间(Metaspace)是Java虚拟机(JVM)中用于存放类的元数据的区域,从Java 8开始引入,替代了之前的永久代(PermGen)
图中红色箭头所指就是元空间

元空间是方法区在HotSpot JVM 中的实现,方法区主要用于存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。
不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。理论上取决于32位/64位系统可虚拟的内存大小,可见也不是无限制的,需要配置参数。
元空间(Metaspace) 垃圾回收,会对僵死的类及类加载器的垃圾回收会进行回收,元空间(Metaspace) 垃圾回收的时机是,在元数据使用达到“MaxMetaspaceSize”参数的设定值时进行。
元空间OOM的现象
JVM 在启动后或者某个时间点开始,MetaSpace 的已使用大小在持续增长,同时每次 GC 也无法释放,调大 MetaSpace 空间也无法彻底解决。
元空间OOM的核心原因:生成了大量动态类
比如:
使用大量动态生成类的框架(如某些ORM框架、动态代理技术、热部署工具等)
程序代码中大量使用反射,反射在大量使用时,因为使用缓存的原因,会导致ClassLoader和它引用的Class等对象不能被回收
例如下面的生成大量动态代理类的代码示例,则会导致元空间的OOM
// 使用CGLIB动态生成大量类导致元空间溢出
public class MetaspaceOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create(); // 动态生成类并加载
}
}
static class OOMObject {
// 这里可以是一些业务方法
}
}
元空间(Metaspace) OOM 解决办法:
减少程序中反射的大量使用
做好熔断限流措施,对应用做好过载保护,比如阿里的sentinel限流熔断中间件
场景三:堆外内存OOM
Java对外内存(Direct Memory)OOM指的是Java直接使用的非堆内存(off-heap memory)耗尽导致的OutOfMemoryError。这部分内存主要用于Java NIO库,允许Java程序以更接近操作系统的方式管理内存,常用于高性能缓存、大型数据处理等场景
例如下面的代码,如何堆外内存太小,就会导致堆外内存的OOM:
// 分配大量直接内存导致OOM
import java.nio.ByteBuffer;
public class DirectMemoryOOM {
private static final int ONE_MB = 1024 * 1024;
public static void main(String[] args) {
int count = 1;
try {
while (true) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(ONE_MB);
count++;
}
} catch (Exception e) {
System.out.println("Exception: instance created " + count);
throw e;
}
}
}
堆外内存的原因
- 分配过量的直接内存:程序中大量使用DirectByteBuffer等直接内存分配方式,而没有相应的释放机制,导致内存迅速耗尽,常见于NIO、Netty等相关组件。
- 内存泄露:如果分配的直接内存没有被及时释放(例如,ByteBuffer未被回收),就可能发生内存泄露。
- JVM对外内存限制设置不当:通过
-XX:MaxDirectMemorySize参数控制对外内存大小,如果设置过小,可能无法满足应用需求。
堆外内存OOM的解决方案
- 合理设置对外内存大小:根据应用的实际需求调整
-XX:MaxDirectMemorySize参数,给予足够的直接内存空间。 - 优化内存使用:减少不必要的直接内存分配,重用DirectByteBuffer等资源。
- 内存泄露排查:使用工具(如VisualVM、JProfiler等)定位和解决内存泄露问题。
- 代码优化:确保使用完直接内存后显式调用
sun.misc.Cleaner.clean()或通过其他机制释放内存。
最后说一句(求关注,求赞,别白嫖我)
最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。
这是大佬写的, [7701页的BAT大佬写的刷题笔记,让我offer拿到手软](程序员江小北的笔记-江小北的资料馆 (aijiangsir.com))
本文,已收录于,我的技术网站 [aijiangsir.com](程序员江小北的笔记-江小北的资料馆 (aijiangsir.com)),有大厂完整面经,工作技术,架构师成长之路,等经验分享
求一键三连:点赞、分享、收藏
点赞对我真的非常重要!在线求赞,加个关注我会非常感激!
美团面试:说说OOM三大场景和解决方案? (绝对史上最全)的更多相关文章
- Redis与DB的数据一致性解决方案(史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...
- HR面试:过五关斩六将后,小心阴沟翻船!(史上最全、避坑宝典)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
- 46道史上最全Redis面试题,面试官能问的都被我找到了(含答案)
Redis高性能缓存数据库 1.什么是 Redis?简述它的优缺点? Redis 的全称是:Remote Dictionary.Server,本质上是一个 Key-Value 类型的内存数据库,很像m ...
- 50道Redis面试题史上最全,以后面试再也不怕问Redis了
1.什么是Redis? Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存. ...
- (转)史上最全 40 道 Dubbo 面试题及答案,看完碾压面试官!
背景:因为自己的简历写了dubbo,面试时候经常被问到.实际自己对dubbo的认识只停留在使用阶段,所以有必要好好补充下基础的理论知识. https://zhuanlan.zhihu.com/p/45 ...
- 应届生应聘阿里,腾讯,美团90%会被问到的Netty面试题!史上最全系列!
1.BIO.NIO 和 AIO 的区别? BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理.线程开销大.伪异步 IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源 ...
- 史上最全 40 道 Dubbo 面试题及答案,看完碾压面试官!
想往高处走,怎么能不懂 Dubbo? Dubbo是国内最出名的分布式服务框架,也是 Java 程序员必备的必会的框架之一.Dubbo 更是中高级面试过程中经常会问的技术,无论你是否用过,你都必须熟悉. ...
- 史上最全 40 道 Dubbo 面试题及答案,看完碾压面试官
想往高处走,怎么能不懂 Dubbo? Dubbo是国内最出名的分布式服务框架,也是 Java 程序员必备的必会的框架之一.Dubbo 更是中高级面试过程中经常会问的技术,无论你是否用过,你都必须熟悉. ...
- 史上最全Spring面试71题与答案
1.什么是spring? Spring是个java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring框架目标是简化Java企业 ...
- 应聘阿里,字节跳动美团90%会问到的JVM面试题! 史上最全系列!
Java 内存分配 • 寄存器:程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码.• 静态域:static 定义的静态成员.• 常量池:编译时被确定并保存在 .class 文件中的(f ...
随机推荐
- [转帖]探索惊群 ④ - nginx - accept_mutex
https://wenfh2020.com/2021/10/10/nginx-thundering-herd-accept-mutex/ 由主进程创建的 listen socket,要被 fork ...
- DashBoard in k8s 简单使用
DashBoard in k8s 简单使用 第一部分 拉取分发镜像 没办法的事情,公司网络实在是太垃圾了, dockerhub 又不让多次docker pull 找一台网络表好的机器 执行如下命令: ...
- 热更新适配ibatis原理浅析
一.热更新解决了什么问题? 在研发过程中,每个研发同学在联调.自测阶段中总会频繁的去执行编译.构建.打包的动作,遇到比较大的项目,执行一套流程下来,往往需要3-10分钟左右,极大的降低了研发的速度,基 ...
- Hutool中那些常用的工具类和实用方法
背景 灵魂拷问1:还在为新项目工具类搬迁而烦恼? 灵魂拷问2:还在为项目中工具类维护而烦恼? 简述 **Hutool**它是一个Java工具集类库,包含了很多静态方法的封装:流处理.时间日期处理.正则 ...
- 手写promise完成异常处理和状态只能够修改一次
8.处理 Promise 抛出的异常 <script src="./Promise.js"></script> <script type=" ...
- elementui中自定义Select选择器样式自定义
<el-select class="my-el-select" v-model="tenantCont" placeholder="请输入机构标 ...
- 【JS 逆向百例】网洛者反爬练习平台第一题:JS 混淆加密,反 Hook 操作
关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...
- 数字预失真(DPD)小试
前言 射频功放的增益响应并非线性的,受到放大管饱和效应的影响,功放不可避免地出现非线性.甚至具有记忆效应的失真.这种非线性失真不仅产生高阶谐波,还会产生互调干扰,降低带内信噪比,影响带外信号.因此,需 ...
- TienChin 代码格式化-项目结构大改造
代码格式化 博主下载项目之后发现,整体的代码格式化风格,与 C 那种语言很相似,说明这个作者之前就是从事这块的导致风格有点类似,我们来格式化一下,当然这不是必要的,我是没习惯这种写法所以这里我写一下我 ...
- 《最新出炉》系列初窥篇-Python+Playwright自动化测试-37-如何截图-上篇
1.简介 这个系列的文章也讲解和分享了差不多三分之一吧,突然有小伙伴或者童鞋们问道playwright有没有截图的方法.答案当然是:肯定有的.宏哥回过头来看看确实这个非常基础的知识点还没有讲解和分享. ...