哪些场景会发生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

相关文章

nginx -t、nginx -s stop 和 nginx -s reload 命令的详细解析(结合应用场景)

《nginx-t、nginx-sstop和nginx-sreload命令的详细解析(结合应用场景)》本文解析Nginx的-t、-sstop、-sreload命令,分别用于配置语法检... 以下是关于 nginx -t、nginx -s stop 和 nginx -s reload 命令的详细解析,结合实际应

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

MyBatis-Plus 中 nested() 与 and() 方法详解(最佳实践场景)

《MyBatis-Plus中nested()与and()方法详解(最佳实践场景)》在MyBatis-Plus的条件构造器中,nested()和and()都是用于构建复杂查询条件的关键方法,但... 目录MyBATis-Plus 中nested()与and()方法详解一、核心区别对比二、方法详解1.and()

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛