常见内存溢出原因及解决思路
内存溢出(OOM)通常出现在某一块内存空间耗尽的时候,导致内存溢出的原因有很多,常见的有堆溢出、直接内存溢出、永久区溢出等。
堆溢出
堆是Java程序中最为重要的内存空间,由于大量的对象都直接分配在堆上,因此它也成为最有可能发生溢出的区间。一般来说,绝大部分Java的内存溢出都属于这种情况。其原因是因为大量对象占据了堆空间,而这些对象都持有强引用,导致无法回收,当对象大小之和大于由Xmx参数指定的堆空间大小时,溢出错误就自然而然地发生了。
【示例】ArrayList对象持有byte数组的强引用,导致数据无法回收。 -Xms5m -Xmx20m
public class SimpleHeapOOM {public static void main(String[] args) {List list = new ArrayList();for (int i = 0; i < 1024; i++) {list.add(new byte[1024 * 1024]);}}
}
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat com.example.demo.jvm.SimpleHeapOOM.main(SimpleHeapOOM.java:16)
为了缓解堆溢出错误,一方面可以使用-Xmx参数指定一个更大的堆空间,另一方面,由于堆空间不可能无限增长,通过下文提到的MAT或者Visual VM等工具,分析找到大量占用堆空间的对象,并在应用程序上做出合理的优化也是十分必要的。
直接内存溢出
在Java的NIO中,支持直接内存的使用,可以使用Java代码获得一块堆外内存空间,这块空间是直接向操作系统申请的。直接内存的申请速度比堆内存慢,但是其访问速度要快于堆内存。因此,对于那些可复用的,并且会被经常访问的空间,使用直接内存是可以提高系统性能的。但由于直接内存没有被Java虚拟机完全托管,若使用不当,也容易触发直接内存溢出,导致宕机。
【示例】下面的代码不断地申请直接内存,并最终可能导致内存溢出
public class DirectBufferOOM {public static void main(String[] args) {for (int i = 0; i < 10240; i++) {ByteBuffer.allocateDirect(1024 * 1024);System.out.println(i);
// System.gc();}}
}
注意代码第6行,System.gc(暂时被注释掉,也就是不会显式触发GC。接着,在Windows平台上,使用32位Java虚拟机,根据以下参数运行上述代码:-Xmx1G -XX:+PrintGCDetails
不用多久,程序就会因为内存溢出而退出,部分打印信息如下:
Exception in thread "main" java. lang . OutOfMemoryError
at java.nio. DirectByteBuffer. <init> (DirectByteBuffer. java:127)
at java.nio. ByteBuffer . allocateDirect (ByteBuffer.java:306)
at geym. zbase. ch7. oom. Di rectBuf ferOOM. main (DirectBuffer0OM. java:14)
可以看到,在大约733次循环时,发生OutOfMemoryError 错误。从堆栈可以看到,发生OOM时,正在进行DirectByteBuffer的分配。
读者也许还会有一个疑问,就是在这里为什么Java的垃圾回收机制没有发挥作用?程序第12行分配的直接内存并没有被任何对象所引用,为何没有被回收呢?从程序的输入日志中也可以看到,虽然打开了-XX:+PrintGCDetails开关,但是并没有一-次GC日志,这说明在整个执行过程中,GC并没有进行。事实上,直接内存不一- 定能够触发GC (除非直接内存使用量达到了-XX:MaxDirectMemorySize的设置),所以保证直接内存不溢出的方法是合理地进行Full GC的执行,或者设定一- 个系统实际可达的-XX:MaxDirectMemorySize值(默认情况下等于-Xmx的设置)。因此,如果系统的堆内存少有GC发生,而直接内存申请频繁,会比较容易导致直接内存溢出(这个问题在32位虚拟机上尤为明显)。如果将上述代码中第6行的System.gc()的注释去掉,使显式GC生效,那么程序将可以正常结束,这说明GC可以回收直接内存。另一个让该程序正常执行的方法是设置一个较 小的堆,在不指定-XX:MaxDirectMemorySize的情况下,最大可用直接内存等于-Xmx的值。
- Xmx512m -XX: +PrintGCDetails
这里将最大堆限制在512MB,而非1GB,这种情况下,最大可用直接内存也为512MB, 操作系统可以同时为堆和直接内存提供足够的空间,当直接内存使用量达到512MB时,也会进行GC释放无用内存空间。
此外,显式设置-XX:MaxDirectMemorySize也是解决这一问题的方法。 只要设置一个系统实际可达的最大直接内存值,那么像这种实际上不应该触发的内存溢出就不会发生了。
综上所述,为避免直接内存溢出,在确保空间不浪费的基础上,合理得执行显式GC,可以降低直接内存溢出的概率,设置合理的-XX:MaxDrectMemorySize也可以避免意外的内存溢出发生,而设置一个较小的堆在32位虚拟机上可以使得更多的内存用于直接内存。
过多线程导致OOM
由于每一个线程的开启都要占用系统内存,因此当线程数量太多时,也有可能导致OOM。由于线程的栈空间也是在堆外分配的,因此和直接内存非常相似,如果想让系统支持更多的线程,那么应该使用一个较小的堆空间。
【示例】需要在32位操作系统下运行 jdk1.7 -Xmx1g
public class MultiThreadOOM {public static class SleepThread implements Runnable {@Overridepublic void run() {try {Thread.sleep(10000000);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {for (int i = 0; i < 1500000; i++) {new Thread(new SleepThread(), "Thread: " + i).start();System.out.println("Thread:" + i + " created");}}
}
运行结果:
永久区溢出
永久区(Perm) 是存放类元数据的区域。如果一 一个系统定了太多的类型,那么永久区是有可能溢出的。在JDK 1.8中,永久区被一块称为元数据的区域替代, 但是它们的功能是类似的,都是为了保存类的元信息。
【示例】Jdk1.6执行,1.8已经去掉了-XX:MaxPermSize
public class PermOOM {public static void main(String[] args) {for (int i=0;i<100000;i++){Apple apple = Apple.builder().id(i).build();}}
}
运行结果:
解决可以从以下方面考虑:
- 增加MaxPermSize的值
- 减少系统需要的类的数量
- 使用ClassLoader合理地装载各个类,并定期回收
常见内存溢出原因及解决思路相关推荐
- jmeter(二十二)内存溢出原因及解决方法
jmeter(二十二)内存溢出原因及解决方法 参考文章: (1)jmeter(二十二)内存溢出原因及解决方法 (2)https://www.cnblogs.com/imyalost/p/7901064 ...
- 内存学习(二)内存溢出介绍以及解决思路
文章目录 一.内存溢出定义 二.内存溢出原因 三.常见的一些内存溢出风险 3.1 strcpy 函数 3.2 sprintf函数 3.3 malloc函数 一.内存溢出定义 指程序在申请内存时,没有足 ...
- java内存溢出原因及解决_java内存溢出的原因和解决方法
java内存溢出的原因和解决方法 发布时间:2020-06-15 17:57:39 来源:亿速云 阅读:85 作者:元一 内存溢出含义: 内存溢出(out of memory)通俗理解就是内存不够,通 ...
- Spark开发-Spark内存溢出原因以及解决方式
Dpark内存溢出 Spark内存溢出 堆内内存溢出 堆外内存溢出 堆内内存溢出 java.lang.OutOfMemoryError: GC overhead limit execeeded jav ...
- Android 系统(87)---常见的内存泄漏原因及解决方法
常见的内存泄漏原因及解决方法 (Memory Leak,内存泄漏) 为什么会产生内存泄漏? 当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被 ...
- 常见的内存泄漏原因及解决方法
常见的内存泄漏原因及解决方法 参考文章: (1)常见的内存泄漏原因及解决方法 (2)https://www.cnblogs.com/leeego-123/p/12187677.html 备忘一下.
- mysql 死锁原因_Mysql并发时经典常见的死锁原因及解决方法
1.mysql都有什么锁 MySQL有三种锁的级别:页级.表级.行级. 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突的概率最高,并发度最低. 行级锁:开销大,加锁慢:会出现死锁:锁定粒 ...
- 常见的 OOM 原因及其解决方法(OutOfMemoryError)
当 JVM 内存严重不足时,就会抛出 java.lang.OutOfMemoryError 错误.本文总结了常见的 OOM 原因及其解决方法,如下图所示.如有遗漏或错误,欢迎补充指正. 1.Java ...
- springboot上传大文件时内存溢出的可能解决办法
springboot上传大文件时内存溢出的可能解决办法 在springboot中上传大文件时要考虑内存的情况,一般我们会通过在执行服务时加入-Xms512m -Xmx512m等参数加大堆内存,但这是指 ...
最新文章
- linux if 命令判断条件总结
- 数据科学 | Python数据科学常用库
- python脚本多线程爬虫爬电脑壁纸
- 复旦计算机学院徐老师,复旦大学信息科学与技术学院徐跃东副研究员到课题组访问交流...
- 的注册表怎么才能删干净_袜子怎么洗才能洗干净,你需要这些技巧
- 【重磅】央行发大招!最全面的支付安全风险大检查来了……
- BugKuCTF 杂项 telnet
- EventTrigger接管所有事件导致其他事件无法触发
- 推荐:万能模板,十分钟打造电商首焦Banner
- Python来处理数独游戏(含世界最难数独示例)
- Linux在终端搜索的两条命令
- 29.TCP/IP 详解卷1 --- 网络文件系统
- android gradle 离线安装,Android Studio离线配置gradle(附gradle下载地址)
- 吴伯凡-认知方法论-认知是一个长期修炼的过程
- java 生成2位随机数_java生成随机数保留数点后两位
- 【知识图谱】实践篇——基于医疗知识图谱的问答系统实践(Part2):图谱数据准备与导入
- 三角测量的一些基础理论
- 没有网能使用mysql吗_就用本地数据库;即使在没有联网的电脑上也可以放心使用...
- QGIS制图表达-符号大小随比例尺变化
- 旅游网后台管理系统(三)权限操作