哪些场景会发生OOM异常

2024-04-27 05:36
文章标签 异常 场景 发生 oom

本文主要是介绍哪些场景会发生OOM异常,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

刚刚开通了一个公众号,会分享一些技术博客和自己觉得比较好的项目,同时会更新一些自己使用的工具和图书资料,后面会整理一些面试资料进行分享,觉得有兴趣的可以关注一下。
在这里插入图片描述

文章目录

  • 前言
  • 1.堆内存溢出
  • 2.栈内存异常
  • 3.直接内存溢出
  • 4. 元空间溢出
  • 5.GC OOM


前言

从今天开始会整理一些常见的面试题目,博客中会涉及一些JVM参数,可以关注一下公众号,回复JVM,即可领取最新版《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》

今天要聊的就是比较经典的OOM问题。主要有5种场景,喜下面分别介绍。


1.堆内存溢出

这是比较常见的一种内存溢出的异常。

堆内存主要用来存储对象实例,我们只要不停的创建对象,并且保证GCROOTS和对象之间有可达路径避免被垃圾回收的话,那么在对象数量超过最大堆的大小限制之后,很快就能出现这个异常。我们可以通过设置堆内存位2M-Xms2m -Xmx2m,然后执行测试代码:

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

异常描述如下:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.base/java.util.Arrays.copyOf(Arrays.java:3512)at java.base/java.util.Arrays.copyOf(Arrays.java:3481)at java.base/java.util.ArrayList.grow(ArrayList.java:237)at java.base/java.util.ArrayList.grow(ArrayList.java:244)at java.base/java.util.ArrayList.add(ArrayList.java:454)at java.base/java.util.ArrayList.add(ArrayList.java:467)at OOM.main(OOM.java:8)

2.栈内存异常

栈是线程私有的,他的生命周期和线程相同,每个方法在执行的同时都会创建一个栈帧,栈帧里面保存了局部变量表、操作数栈、动态链接、方法出口。那方法在调用的过程呢,其实就是栈帧入栈和出栈的过程。
在Java虚拟机规范中对虚拟机栈定义了两种异常。

  • 第一,如果线程请求的栈深度大于虚拟机所允许的深度,就会抛出StackOverflow异常。比如我们可以设置-Xss160k,那其中Xss代表每个线程的栈内存大小,通过测试发现,单线程下无论怎么设置参数,其实都是StackOverflow异常。
public class OOM {private static int length = 1;public static void main(String[] args) {test();}static void test(){System.out.println(length++);test();}
}

结果如下:

15261
Exception in thread "main" java.lang.StackOverflowErrorat java.base/java.io.FileOutputStream.write(FileOutputStream.java:349)at java.base/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81)at java.base/java.io.BufferedOutputStream.flush(BufferedOutputStream.java:142)at java.base/java.io.PrintStream.write(PrintStream.java:570)at java.base/sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:234)at java.base/sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:313)at java.base/sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:111)at java.base/java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:178)at java.base/java.io.PrintStream.writeln(PrintStream.java:723)at java.base/java.io.PrintStream.println(PrintStream.java:938)at OOM.test(OOM.java:12)at OOM.test(OOM.java:13)
  • 第二种,如果虚拟机栈可以动态扩展的话,并且扩展时无法申请到足够的内存,就会抛出OOM异常。现在我们把代码改成多线程,并且调整参数为2m,-Xss2m,就可以看见OOM异常了,因为每个线程分配的内存越大,栈空间可容纳的线程总数量就越少,越容易产生内存溢出,那反之的话如果内存不够的情况,可以调小该参数来达到支撑更多线程的目的。
public class OOM {public static void main(String[] args) {while (true){new Thread(() -> dontStop()).start();}}static void dontStop(){try {TimeUnit.HOURS.sleep(5);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

结果如下:

JVMDUMP039I ????????"systhrow",????"java/lang/OutOfMemoryError",?? 2024/04/26 17:19:54 - ????

3.直接内存溢出

直接内存并不是虚拟机运行时数据区域的一部分,也不是《Java虚拟机规范》中内定义的内存区域,并且不受堆内存大小的限制,但是受到机器内存大小的限制。比如常见在NIO中可以使用native函数直接分配堆外内存,就有可能导致OOM的问题。那直接内存的大小可以通过参数来指定,那如果你不指定的话,他默认就和java堆内存最大值一样,直接内存导致内存溢出一个最明显的特征就是dump文件不会有明显的异常,如果发现OOM之后dump文件很小,而程序中又直接或间接使用了NIO,那么就可以考虑检查一下是不是这方面的原因。


异常信息如下:

java.lang.OutOfMemoryError: Direct buffer memory

代码里使用DirectByteBuffer之类的,如下:

public class OOM {public static void main(String[] args) {List<ByteBuffer> list = new ArrayList<>();while (true){list.add(ByteBuffer.allocate(1024*1024*20));}}
}

4. 元空间溢出

JDK8之后使用Metaspace来代替永久代,Metaspace是方法区在Hotspot的实现。Metaspace不在虚拟机内存,而是使用本地内存,也就是JDK8中的ClassMetadata,被存储在叫做Metaspacenativr memory
比如我们可以这样设置元空间的大小
-XX:MetaspaceSize=50m -XX:MaxMetaspaceSize=50m(1.8版本jdk)
1.8版本之前方法区存在于永久代,1.8版本之后取消了永久代的概念,如果是之前的版本的话,可以通过设置PermSize大小来设置永久代的大小。
代码:

public class OOM {public static void main(String[] args) {while (true){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OOM.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {return methodProxy.invokeSuper(o, objects);}});enhancer.create();}}}

结果:

Exception in thread "main" org.springframework.cglib.core.CodeGenerationException: java.lang.OutOfMemoryError-->Metaspaceat org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:511)at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:585)at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:131)at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319)at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572)at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:387)at OOM.main(OOM.java:23)

5.GC OOM

GC OOM是由于JVM在GC时,对象过多导致内存溢出。
代码如下:

public class OOM {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(15);for (int i = 0; i < Integer.MAX_VALUE; i++) {executorService.execute(() -> {try {Thread.sleep(20000);} catch (InterruptedException e) {throw new RuntimeException(e);}});}}}

异常信息:

java.lang.OutOfMemoryError: GC overhead limit

这篇关于哪些场景会发生OOM异常的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/939638

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Python中 try / except / else / finally 异常处理方法详解

《Python中try/except/else/finally异常处理方法详解》:本文主要介绍Python中try/except/else/finally异常处理方法的相关资料,涵... 目录1. 基本结构2. 各部分的作用tryexceptelsefinally3. 执行流程总结4. 常见用法(1)多个e

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

Java利用@SneakyThrows注解提升异常处理效率详解

《Java利用@SneakyThrows注解提升异常处理效率详解》这篇文章将深度剖析@SneakyThrows的原理,用法,适用场景以及隐藏的陷阱,看看它如何让Java异常处理效率飙升50%,感兴趣的... 目录前言一、检查型异常的“诅咒”:为什么Java开发者讨厌它1.1 检查型异常的痛点1.2 为什么说

Spring Security 前后端分离场景下的会话并发管理

《SpringSecurity前后端分离场景下的会话并发管理》本文介绍了在前后端分离架构下实现SpringSecurity会话并发管理的问题,传统Web开发中只需简单配置sessionManage... 目录背景分析传统 web 开发中的 sessionManagement 入口ConcurrentSess

Java异常捕获及处理方式详解

《Java异常捕获及处理方式详解》异常处理是Java编程中非常重要的一部分,它允许我们在程序运行时捕获并处理错误或不预期的行为,而不是让程序直接崩溃,本文将介绍Java中如何捕获异常,以及常用的异常处... 目录前言什么是异常?Java异常的基本语法解释:1. 捕获异常并处理示例1:捕获并处理单个异常解释:

99%的人都选错了! 路由器WiFi双频合一还是分开好的专业解析与适用场景探讨

《99%的人都选错了!路由器WiFi双频合一还是分开好的专业解析与适用场景探讨》关于双频路由器的“双频合一”与“分开使用”两种模式,用户往往存在诸多疑问,本文将从多个维度深入探讨这两种模式的优缺点,... 在如今“没有WiFi就等于与世隔绝”的时代,越来越多家庭、办公室都开始配置双频无线路由器。但你有没有注