前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >剖析Java OutOfMemoryError异常

剖析Java OutOfMemoryError异常

作者头像
王金龙
发布于 2020-03-04 11:00:14
发布于 2020-03-04 11:00:14
2K00
代码可运行
举报
文章被收录于专栏:王金龙的专栏王金龙的专栏
运行总次数:0
代码可运行

剖析Java OutOfMemoryError异常

在JVM中,除了程序计数器外,虚拟机内存中的其他几个运行时区域都有发生OutOfMemoryError异常的可能,本篇就来深入剖析一下各个区域出现OOM异常的情形,以及如何解决各个区域的OOM问题。

本篇主要包括如下内容:

  • Java堆溢出
  • 运行时常量池和方法区溢出
  • 本地内存溢出

Java堆溢出

Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免JVM清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生溢出异常。

堆溢出复现

要复现这种情况也很简单:将Java堆的大小限制为固定值,且不可扩展(将堆的最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展);当使用一个 while(true) 循环来不断创建对象就会发生 OutOfMemory,还可以使用 -XX:+HeapDumpOutofMemoryErorr 当发生 OOM 时会自动 dump 堆栈到文件中。

测试代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
        List<String> list = new ArrayList<>() ;
        while (true){
            list.add("1") ;
        }
    }

运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3210)
    at java.util.Arrays.copyOf(Arrays.java:3181)
    at java.util.ArrayList.grow(ArrayList.java:265)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
    at java.util.ArrayList.add(ArrayList.java:462)
    at Main.main(Main.java:13)

Process finished with exit code 1

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space即是说发生了堆溢出。

原因
  1. 代码中可能存在大对象分配 ;
  2. 可能存在内存泄露,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象;
  3. 如果不是以上两种情况,也就是说内存中的对象都必须存活,就应当检查虚拟机的堆参数(-Xmx与-Xms),是否设置的堆内存空间太小,以及检查代码中是否存在某些对象声明周期过长、持有状态时间过长的情况。

上面复现代码产生堆溢出的原因主要是第三点。

解决方法
  1. 检查是否存在大对象的分配,最有可能的是大数组分配;
  2. 通过jmap命令,把堆内存dump下来,使用mat工具分析一下,检查是否存在内存泄露的问题
  3. 如果没有找到明显的内存泄露,使用 -Xmx 加大堆内存;
  4. 还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性。

运行时常量池和方法区溢出

运行时常量池是方法区的一部分,我们先对运行时常量池溢出进行测试。

运行时常量池溢出复现

最典型的使用运行时常量池的方法是String的intern()方法,该方法是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String包含的字符串添加到常量池中,并且返回此String对象的引用。

在JDK1.6及以前的版本中,由于常量池分配在永久代中,可以通过-XX:PermSize和-XX:MaxPermSIze限制方法区大小,从而限制其中常量池的容量

测试代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        int i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
        }
    }

笔者所用为JDK1.8,已经去除了对这两个JVM参数的支持,程序执行的结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10m; support was removed in 8.0

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10m; support was removed in 8.0

暂不做深究。

方法区溢出复现

方法区用于存放class的相关信息,包括类名、访问修饰符、常量池、字段描述、方法描述等。可以通过借助CGLib直接操作字节码运行时生成大量的动态类,来填满方法区。

PermSize 和 MaxPermSize 已经不能使用了,那在JDK1.8中怎么设置方法区大小呢?

JDK 8 中将类信息移到了本地堆内存(Native Heap)中,将原有的永久代移动到了本地堆中成为 MetaSpace ,如果不指定该区域的大小,JVM 将会动态的调整。

可以使用 -XX:MaxMetaspaceSize=10M 来限制最大元空间。这样当不停的创建类时将会占满该区域并出现 OOM。

测试代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
        while (true){
            Enhancer  enhancer = new Enhancer() ;
            enhancer.setSuperclass(Main.class);
            enhancer.setUseCache(false) ;
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invoke(o,objects) ;
                }
            });
            enhancer.create() ;
        }
    }

设置好JVM参数后,执行上述代码,得到下面的额结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
    at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:530)
    at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)
    at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:582)
    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:569)
    at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:384)
    at com.etekcity.cloud.Main.main(Main.java:27)

Process finished with exit code 1

这里的 OOM 伴随的是 Exception in thread "main" java.lang.OutOfMemoryError: Metaspace 也就是元空间溢出。

方法区溢出在应用中是比较常见的OOM异常,Spring、Hibernate等框架在对类进行增强时,都会使用到CGLib技术来增强类,增强的类越多,对方法区的容量要求就越大,就越可能出现方法区的OOM异常。

解决方法

因为该OOM原因比较简单,解决方法有如下几种:

  1. 检查是否永久代空间或者元空间设置的过小;
  2. 检查代码中是否存在大量的反射操作;
  3. dump之后通过mat检查是否存在大量由于反射生成的代理类;
  4. 重启JVM。

本机内存溢出

以上OOM异常都是出现于JVM内部,那么如果是机器本身分给JVM的内存不够导致溢出呢。

机器本身分给JVM的内存容量可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值(-Xmx指定一样)。

可以通过反射获取Unsafe实例进行内存分配,测试代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) throws IllegalAccessException {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        while (true) {
            unsafe.allocateMemory(1024 * 1024);
        }
    }

运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Exception in thread "main" java.lang.OutOfMemoryError
    at sun.misc.Unsafe.allocateMemory(Native Method)
    at Main.main(Main.19)

有DirectMemory导致的内存溢出,在Heap Dump文件中不会看到明显的异常,如果发现OOM之后的dump文件很小,可以考虑一下是否是这方面的原因。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-02-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python 3.10 正式发布,新增模式匹配,同事用了直呼真香!
前几天,也就是 10 月 4 日,Python 发布了 3.10.0 版本,什么?3.9 之后居然不是 4.0?(手动狗头)其实龟叔(Guido van Rossum,吉多·范罗苏姆,Python 之父)早在去年 9 月就说了:
K哥爬虫
2021/10/18
8070
Python 3.10 正式发布,新增模式匹配,同事用了直呼真香!
Python 3.10来了,switch语法终于出现
对于从事数据科学和人工智能领域的人们来说,Python 是大家的首选编程语言。根据最近的一项调查,27% 的程序员开发职位要求掌握 Python 语言,今年年初这一数字还只是 18.5%。
机器之心
2021/04/21
7790
Python 3.10来了,switch语法终于出现
错误调试精确到行、match-case模式匹配……Python 3.10正式版真的很友好
Python 是当今最流行的编程语言之一,已被广泛用于各种领域和应用,从学习计算机科学的基础知识到执行复杂而直接的科学计算任务,再到构建游戏,它的影子无处不在。Python 甚至应用于数据科学和量子计算等更高级的领域。
机器之心
2021/10/15
8140
错误调试精确到行、match-case模式匹配……Python 3.10正式版真的很友好
Python 3.10的几个好用的新特性
3.10版没有添加新的模块,但是引入了很多新的并且有用的特性。让我们来一起看看吧。
deephub
2021/10/09
5810
Python 3.10的几个好用的新特性
看完 Python3.10 的新特性,我决定仍不更新
Python3.10 在 2021 年的 10 月 3 号发布,目前已经过去 1 个月了,关于它的新特性相信大家已经有所耳闻,不过我决定仍然不更新,目前我在用的版本是 Python3.8,没有任何不爽。下面说一说我不更新的理由。
somenzz
2021/11/12
3.9K0
半年后,再谈 Python3.10
2021 年 10 月 4 号,Python 官方正式发布了 Python3.10.0(https://www.python.org/downloads/release/python-3100/)。当时在忙着大数据相关的工作就没有写文章聊聊这个版本。就以这篇文章来简单聊聊。
哒呵呵
2022/04/14
8060
Python 高级教程之结构化模式匹配
Python 3.10 开始充满了许多令人着迷的新特性。其中一个特别引起了我的注意——结构模式匹配——或者我们大多数人都知道的 switch/case 语句。
海拥
2022/05/07
7140
Python 3.10发布临近,一文尽览所有重要新特性和变化
Python 3.10 的发布日益临近,是时候来看看它将带来的最重要的新特性和变化了。内容包括类型检查,类型别名,switch/case语法,数量统计,上下文管理器,性能等。
McGL
2021/04/21
8160
Python 3.10刚发布,这5点非常值得学习!
Python 3.10以前,它是这样提示的,你可能完全不知道哪里有问题,当代码过多。
快学Python
2021/10/18
3430
Python 3.10刚发布,这5点非常值得学习!
Python 3.10来了,switch语法终于出现
对于从事数据科学和人工智能领域的人们来说,Python 是大家的首选编程语言。根据最近的一项调查,27% 的程序员开发职位要求掌握 Python 语言,今年年初这一数字还只是 18.5%。
AI算法与图像处理
2021/04/21
4990
Python 3.10来了,switch语法终于出现
Python3.10 结构化模式匹配 PEP 634
眼看 2021 马上结束,python 发布了它的 3.10 版本,优化了错误消息,上下文管理器等内容,但更吸引我的还是结构化模式匹配。
JuneBao
2022/10/26
3390
Python 3.10 来了,居然有这新特性!
大家好,我是一行 已经达到了而立之年的Python,最近发布了3.10版本 新版本添加了很多新的特性,其中最大的特性就莫过于还是安装新版本之后有些python包不支持还得回到原来版本🐶 不过话说回来,除了部分包不兼容以外,还是有很多好用的性能提升 其中最有帮助的就是就是报错更加智能化了 例如语法错误,很多年前作为小白的我遇到这个语法错误问题还跑过去问老师 毕竟百度搜索语法错误该怎么办,它回答不上来,最后老师给我指出来原因是少一个加一个括号,顿时无比尴尬 后来python性能优化给出了错误的位置在哪,但是有些
行哥玩Python
2021/10/21
1.4K0
Python 3.10 来了,居然有这新特性!
Python 3.10 的新功能:模式匹配 Pattern Matching
2021 年 3 月 2 日的时候,Guido 发推说 3.10.0a6 出来了,文档也已经有了,4 月 5 日会释出 a7,5 月 3 日出 b1。
Alan Lee
2021/03/22
6630
Python 3.10 的新功能:模式匹配 Pattern Matching
Python基础-3 流程控制
流程控制即控制代码执行的顺序。Python中的流程控制一般通过判断、循环语句实现。
一只大鸽子
2022/12/06
4050
Python基础-3 流程控制
Python 3.11的10个使代码更加高效的新特性
性能有巨大的提升是Python 3.11的一个重要的改进,除此以外Python 3.11还有增加了许多新的特性。在本文中我们将介绍Python 3.11新特性,通过代码示例演示这些技巧如何提高生产力并优化代码。
deephub
2023/08/30
3760
Python 3.11的10个使代码更加高效的新特性
独家 | Python 3.10发布——你应该知道的五大新特性
作者:Varun Singh 翻译:欧阳锦校对:王可汗 本文约1700字,建议阅读5分钟本文为大家介绍了新版本Python的新特性。
数据派THU
2021/12/22
3.2K0
独家 | Python 3.10发布——你应该知道的五大新特性
Python 3.10 和 Python 3.9 之间的差异
在过去的几十年里,Python 在编程或脚本语言领域为自己创造了一个名字。python 受到高度青睐的主要原因是其极端的用户友好性。Python 还用于处理复杂的程序或编码挑战。机器学习 (ML)、人工智能 (AI) 和数据科学等新兴领域也满足了学习这种语言的高需求。与 Java、C# 和其他语言等传统语言相比,Python 是一种强大的编程语言,迅速成为开发人员、数据科学家和 AI/ML 爱好者的最爱。
海拥
2021/12/20
3.7K0
Python3.10版本的新特性介绍
Python近几年来越来越火了,而且版本也一直在不停的更新迭代中。Python在2021/10/04发布了3.10的正式版,虽然你可能还没有升级,不过也可以先大概了解一下在新的版本中做了哪些改动哟。
小博测试成长之路
2021/10/26
5160
Python 3.10 的一些新特性
Python 3.10.0a2 版本已经于 2020-11-04 发布,因此我们可以窥见 Python 3.10 的一些新特性。这些新特性很可能会改变未来的 Python 生态系统,使其朝着更明确,更易读的方向发展,同时保持我们熟知和喜欢的易用性。
somenzz
2020/11/25
1.3K0
Python 3.10 的一些新特性
图解python | 模块
教程地址:http://www.showmeai.tech/tutorials/56
ShowMeAI
2022/02/23
5550
图解python | 模块
推荐阅读
相关推荐
Python 3.10 正式发布,新增模式匹配,同事用了直呼真香!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验