工作中最常见的 6 种 OOM 问题

前言


今天跟大家一起聊聊线上服务出现 OOM 问题的 6 种场景,希望对你会有所帮助。


一、堆内存 OOM


堆内存 OOM 是最常见的 OOM 了。


出现堆内存 OOM 问题的异常信息如下:


java.lang.OutOfMemoryError: Java heap space


此 OOM 是由于 JVM 中 heap 的最大值,已经不能满足需求了。


举个例子:


public class HeapOOMTest {
public static void main(String[] args) { List<HeapOOMTest> list = Lists.newArrayList(); while (true) { list.add(new HeapOOMTest()); } }}


这里创建了一个 list 集合,在一个死循环中不停往里面添加对象。


执行结果:


出现了 java.lang.OutOfMemoryError: Java heap space 的堆内存溢出。


很多时候,excel 一次导出大量的数据,获取在程序中一次性查询的数据太多,都可能会出现这种 OOM 问题。


我们在日常工作中一定要避免这种情况。


二、栈内存 OOM


有时候,我们的业务系统创建了太多的线程,可能会导致栈内存 OOM。


出现堆内存 OOM 问题的异常信息如下:


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


给大家举个例子:


public class StackOOMTest {    public static void main(String[] args) {        while (true) {            new Thread().start();        }    }}


使用一个死循环不停创建线程,导致系统产生了大量的线程。


执行结果:



如果实际工作中,出现这个问题,一般是由于创建的线程太多,或者设置的单个线程占用内存空间太大导致的。


建议在日常工作中,多用线程池,少自己创建线程,防止出现这个 OOM。


三、栈内存溢出


我们在业务代码中可能会经常写一些递归调用,如果递归的深度超过了 JVM 允许的最大深度,可能会出现栈内存溢出问题。


出现栈内存溢出问题的异常信息如下:


java.lang.StackOverflowError

例如:
public class StackFlowTest {    public static void main(String[] args) {        doSamething();    }
private static void doSamething() { doSamething(); }}

执行结果:



出现了 java.lang.StackOverflowError 栈溢出的错误。


我们在写递归代码时,一定要考虑递归深度。即使是使用 parentId 一层层往上找的逻辑,也最好加一个参数控制递归深度。防止因为数据问题导致无限递归的情况,比如:id 和 parentId 的值相等。


四、直接内存 OOM


直接内存不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。


它来源于 NIO,通过存在堆中的 DirectByteBuffer 操作 native 内存,是属于堆外内存,可以直接向系统申请的内存空间。


出现直接内存 OOM 问题时异常信息如下:


java.lang.OutOfMemoryError: Direct buffer memory


例如下面这样的:


public class DirectOOMTest {
private static final int BUFFER = 1024 * 1024 * 20;
public static void main(String[] args) { ArrayList<ByteBuffer> list = new ArrayList<>(); int count = 0; try { while (true) { // 使用直接内存 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER); list.add(byteBuffer); count++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } finally { System.out.println(count); } }}


执行结果:


会看到报出来 java.lang.OutOfMemoryError: Direct buffer memory 直接内存空间不足的异常。


五、GC OOM


GC OOM 是由于 JVM 在 GC 时,对象过多,导致内存溢出,建议调整 GC 的策略。

出现 GC OOM 问题时异常信息如下:


java.lang.OutOfMemoryError: GC overhead limit exceeded


为了方便测试,我先将 IDEA 中的最大和最小堆大小都设置成 10M:


-Xmx10m -Xms10m

例如下面这个例子:


public class GCOverheadOOM {    public static void main(String[] args) {        ExecutorService executor = Executors.newFixedThreadPool(5);        for (int i = 0; i < Integer.MAX_VALUE; i++) {            executor.execute(() -> {                try {                    Thread.sleep(10000);                } catch (InterruptedException e) {                }            });        }    }}


执行结果:


出现这个问题是由于 JVM 在 GC 的时候,对象太多,就会报这个错误。


我们需要改变 GC 的策略。


在老代 80% 时就是开始 GC,并且将 -XX:SurvivorRatio(-XX:SurvivorRatio=8)和 -XX:NewRatio(-XX:NewRatio=4)设置的更合理。


六、元空间 OOM


JDK 8 之后使用 Metaspace 来代替永久代,Metaspace 是方法区在HotSpot中的实现。


Metaspace 不在虚拟机内存中,而是使用本地内存也就是在 JDK 8 中的 ClassMetadata,被存储在叫做 Metaspace 的 native memory。


出现元空间 OOM 问题时异常信息如下:


java.lang.OutOfMemoryError: Metaspace

为了方便测试,我修改一下 IDEA 中的 JVM 参数,增加下面的配置:


-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

指定了元空间和最大元空间都是 10M。


接下来,看看下面这个例子:


public class MetaspaceOOMTest {    static class OOM {    }
public static void main(String[] args) { int i = 0; try { while (true) { i++; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOM.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, args); } }); enhancer.create(); } } catch (Throwable e) { e.printStackTrace(); } }}


执行结果:


程序最后会报 java.lang.OutOfMemoryError: Metaspace 的元空间 OOM。


这个问题一般是由于加载到内存中的类太多,或者类的体积太大导致的。

- EOF -

推荐阅读  点击标题可跳转

1、多线程使用不当导致的 OOM

2、MySQL数据查询太多会OOM吗?

3、面试官:哪些场景会产生OOM?怎么解决?


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

点赞和在看就是最大的支持❤️


相关推荐

  • 分库分表实战
  • Spring Boot集成zxing实现生成二维码功能
  • Java日志系统历史从入门到崩溃
  • 几种常见的 Kafka 集群监控工具
  • 持续移动开发如何从测试自动化中获益
  • 大模型企业怎么“卷”过 | 直播预告
  • 把大模型装进手机,小米、OPPO、vivo 卷起来了!
  • Stable Diffusion核心团队全部离职!Kimi大面积宕机,月之暗面已扩容5次;Redis不再“开源”引争议 |Q资讯
  • 能玩3D游戏的AI玩家!谷歌新智能体SIMA发布,但水平还在“新手村”
  • 微软围着Copilot 转!首款AI PC能算惊艳吗?网友:9分钟宣传片让人昏昏欲睡
  • Redis“叛逃”开源,得罪了几乎所有人
  • 搭建一个符合大厂标准的Vue3+Ts +Vite脚手架
  • JS官方宣布:通过了groupBy方法的提案!开发效率又提升了~
  • AI的大机会
  • Redis 不再开源??
  • JDK 22 发布正式版了
  • 华为某外包:名校毕业,自以为能力吊打华为16级以上员工,年薪总包却只有30万。网友:刚接触职场?自我感觉太良好了?
  • 浅聊一下最接近英伟达B200的AI芯片
  • 72岁法国概率论大佬获阿贝尔奖,陶哲轩:他的知名度理应更高
  • Kaggle竞赛赢得根本停不下来--Psi.