剖析Java OutOfMemoryError异常
剖析Java OutOfMemoryError异常
在JVM中,除了程序计数器外,虚拟机内存中的其他几个运行时区域都有发生OutOfMemoryError异常的可能,本篇就来深入剖析一下各个区域出现OOM异常的情形,以及如何解决各个区域的OOM问题。
本篇主要包括如下内容:
- Java堆溢出
- 运行时常量池和方法区溢出
- 本地内存溢出
Java堆溢出
Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免JVM清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生溢出异常。
堆溢出复现
要复现这种情况也很简单:将Java堆的大小限制为固定值,且不可扩展(将堆的最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展);当使用一个 while(true) 循环来不断创建对象就会发生 OutOfMemory,还可以使用 -XX:+HeapDumpOutofMemoryErorr 当发生 OOM 时会自动 dump 堆栈到文件中。
测试代码:
1 |
public static void main(String[] args) { |
运行结果:
1 |
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space |
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
即是说发生了堆溢出。
原因
- 代码中可能存在大对象分配 ;
- 可能存在内存泄露,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象;
- 如果不是以上两种情况,也就是说内存中的对象都必须存活,就应当检查虚拟机的堆参数(-Xmx与-Xms),是否设置的堆内存空间太小,以及检查代码中是否存在某些对象声明周期过长、持有状态时间过长的情况。
上面复现代码产生堆溢出的原因主要是第三点。
解决方法
- 检查是否存在大对象的分配,最有可能的是大数组分配;
- 通过jmap命令,把堆内存dump下来,使用mat工具分析一下,检查是否存在内存泄露的问题
- 如果没有找到明显的内存泄露,使用 -Xmx 加大堆内存;
- 还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性。
运行时常量池和方法区溢出
运行时常量池是方法区的一部分,我们先对运行时常量池溢出进行测试。
运行时常量池溢出复现
最典型的使用运行时常量池的方法是String的intern()方法,该方法是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String包含的字符串添加到常量池中,并且返回此String对象的引用。
在JDK1.6及以前的版本中,由于常量池分配在永久代中,可以通过-XX:PermSize和-XX:MaxPermSIze限制方法区大小,从而限制其中常量池的容量
测试代码:
1 |
public static void main(String[] args) { |
笔者所用为JDK1.8,已经去除了对这两个JVM参数的支持,程序执行的结果如下:
1 |
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。
测试代码:
1 |
public static void main(String[] args) { |
设置好JVM参数后,执行上述代码,得到下面的额结果:
1 |
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace |
这里的 OOM 伴随的是 Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
也就是元空间溢出。
方法区溢出在应用中是比较常见的OOM异常,Spring、Hibernate等框架在对类进行增强时,都会使用到CGLib技术来增强类,增强的类越多,对方法区的容量要求就越大,就越可能出现方法区的OOM异常。
解决方法
因为该OOM原因比较简单,解决方法有如下几种:
- 检查是否永久代空间或者元空间设置的过小;
- 检查代码中是否存在大量的反射操作;
- dump之后通过mat检查是否存在大量由于反射生成的代理类;
- 重启JVM。
本机内存溢出
以上OOM异常都是出现于JVM内部,那么如果是机器本身分给JVM的内存不够导致溢出呢。
机器本身分给JVM的内存容量可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值(-Xmx指定一样)。
可以通过反射获取Unsafe实例进行内存分配,测试代码如下:
1 |
public static void main(String[] args) throws IllegalAccessException { |
运行结果如下:
1 |
Exception in thread "main" java.lang.OutOfMemoryError |
有DirectMemory导致的内存溢出,在Heap Dump文件中不会看到明显的异常,如果发现OOM之后的dump文件很小,可以考虑一下是否是这方面的原因。
关注我的公众号,获取更多关于面试、技术的文章及福利资源。
剖析Java OutOfMemoryError异常的更多相关文章
- 了解OutOfMemoryError异常 - 深入Java虚拟机读后总结
JVM中的异常发生 Java虚拟机规范中除了程序计数器外,其他几个运行时区域都有发生OutOfMemoryError异常的可能. 本章笔记通过代码来验证Java虚拟机规范中描述的各个运行时区域存储的内 ...
- OutOfMemoryError异常java内存泄漏(Memory Leak)和内存溢出(Memory Overflow)
本篇文章理解源自于<深入理解java虚拟机>2.4章节 实战:OutOfMemoryError异常 在以下例子中,所有代码都可以抛出OutOfMemoryError异常,但是要区分到底 ...
- Java虚拟机学习总结之OutOfMemoryError异常
参考:深入理解java虚拟机一书 开始之前,我们也应当搞清楚连个概念,内存泄漏Memory Leak 内存溢出: 内存泄漏:程序中间动态分配了内存,但是在程序结束时没有释放内存,造成这部分内存不可用. ...
- C#连接solr时提示 java内存异常 (jetty和tomcat哪个更High) java.lang.OutOfMemoryError
C#连接solr时提示 java内存异常 java.lang.OutOfMemoryError 时间:20180130 09:51:13.329,消息:异常消息<?xml version=& ...
- Tomcat部署java项目java.lang.OutOfMemoryError异常解决方法
java.lang.OutOfMemoryError异常解决方法 Window系统环境下,在catalina.bat文件第一行添加以下内容 set JAVA_OPTS=-Xms512m -Xmx512 ...
- 《深入理解java虚拟机》笔记(3)实战:OutOfMemoryError异常
一.Java堆溢出 测试代码: /** * <p>Java堆异常测试</p> * <code>VM Args: -Xms20m -Xmx20m -XX:+HeapD ...
- Java stackoverflowerror异常与outofmemoryerror异常区别
1.stackoverflow: 每当java程序启动一个新的线程时,java虚拟机会为他分配一个栈,java栈以帧为单位保持线程运行状态:当线程调用一个方法是,jvm压入一个新的栈帧到这个线程的栈中 ...
- [java]OutOfMemoryError 原因及解决办法
导致OutOfMemoryError异常的常见原因有以下几种: 内存中加载的数据量过于庞大,如一次从数据库取出过多数据: 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收: 代码中存在死循环 ...
- java的异常
下面是我对Java异常知识的几个小总结,也算是资源回收一下 一.Java异常的知识 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的.比如说,你的代码少了一个分号,那么运 ...
随机推荐
- java.sql.Date与java.sql.Date区别
public static void main(String[] args) { java.sql.Date createTime = new java.sql.Date(System.current ...
- 20191216 GXOI 2019模拟赛 逼死强迫症
题目传送门 分析: sb矩阵加速推一辈子... 想了1个小时,结果好像还和标准答案的方法不一样诶... 标算解法: 老套路,对于新加入的一列,考虑它与目前最后一列的关系 我们可以列出四种方案: 其中前 ...
- LeetCode 11 水池蓄水问题
今天给大家分享的是一道LeetCode中等难度的题,难度不大,但是解法蛮有意思.我们一起来看题目: Link Container With Most Water Difficulty Medium 题 ...
- 利用在线绘制3d几何图形工具分析投影变化
业余写了个在线绘制几何图形工具,工具链接如下: https://tinygltf.xyz/drawgeometry/ 通过脚本代码在可视化窗口添加对应的点,线段,成像平面推到投影后坐标的计算: 点A通 ...
- PSR标准规范
PSR标准规范 基本代码规范 PHP代码文件 必须 以 不带 BOM 的 UTF-8 编码: 类的命名 必须 遵循 StudlyCaps 大写开头的驼峰命名规范: 类中的常量所有字母都 必须 大写,单 ...
- Python - Unittest小结
一.Unittest 单元测试框架,可用于自动化测试用力组织,执行,输出结果 二.Unittest构成 Test Case Test Suite Test Fixture Test Runner (图 ...
- sublime3 docblocker 注释插件的配置
sublime3 docblocker插件定制自己的注释,配置步骤 DocBlockr很好用,不仅仅可以自动生成注释,还可以手动编辑注释的格式. 安装方法: Cmd+Shift+P -> In ...
- Pileup 格式详细说明
转自: https://blog.csdn.net/herokoking/article/details/79276939 Pileup 格式最初是由Sanger Institute的Tony Cox ...
- 看完这篇文章,再次遇到Jedis「Redis客户端」异常相信你不再怕了!
本文导读: [1] 疫情当前 [2] 应用异常监控 [3] Redis客户端异常分析 [4] Redis客户端问题引导分析 [5] 站在Redis客户端视角分析 [6] 站在Redis服务端视角分析 ...
- 浅谈C语言的数据存储(二)
作者:冯老师,华清远见嵌入式学院讲师. 静态区是一个抽象笼统的概念,在实际的Linux/C的可执行程序中并没有静态区这个区域,具体来讲它主要由两个段组成:.data段和.bss段.其中.data段就是 ...