OutOFMemoryError
版权声明:本文为博主原创文章,未经博主允许不得转载。http://www.cnblogs.com/jokermo/
注:本文属于《深入理解java虚拟机》读书笔记,想详细了解请自行看书。
1.Java堆溢出
Java堆用于存储对象实例,只要不断创建对象,并且保证GC Roots到对象之间有可到达路径来避免垃圾回收机制清除这些对象,那么对象数量达到容量限制后就会产生内存溢出异常。
import java.util.ArrayList; import java.util.List; /** * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * create by jokermo **/ public class HeapOOM { static class OOMObject{} public static void main(String[] args){ List<OOMObject> list = new ArrayList<OOMObject>(); while (true){ list.add(new OOMObject()); } } }
运行结果,其中java.lang.OutOfMemoryError:java heap space表示java堆内存溢出异常
![](https://images2018.cnblogs.com/blog/1383286/201805/1383286-20180501230949230-968101372.png)
-Xms20m -Xmx20m:表示java堆大小为20M且不可扩展
解决Java堆异常:
- 首先通过内存映像分析工具(如Eclipse Memory Analyzer)对Dump出的堆存储快照进行分析,主要是确认内存中的对象是否必须的,也就是确定属于内存泄露(Momory Leak)还是内存溢出(Memory Overflow)
- 如果是内存泄露,可进一步通过工具查看泄露对象到GC Roots的引用链,掌握了泄露对象的类型信息及GC Roots引用链信息,就可以比较准确的定位出泄露代码的位置。
- 如果是内存溢出(内存对象还必须活着)
- 查看虚拟机的堆参数(-Xmx 和-Xms),与机器物理内存对比是否还可以调大。
- 从代码上检查是否存在某些对象生命周期过长,持有状态过长的情况,尝试减少程序运行期内存的消耗。
2.虚拟机栈和本地方法栈溢出
虚拟机与本地方法区的两种异常:
- 如果线程请求的栈深度大于虚拟机所运行的最大深度,将抛出StackOverflowError异常
- 如果虚拟机在扩展栈时无法申请到足够的内存空间,将抛出OutOfMemoryError异常
抛出StackOverflowError异常,异常出现时输出的栈深度相应缩小示例:
- 使用-XSS参数减少栈内存容量。
- 定义大量的本地变量,增大此方法帧中本地变量表的长度。
/** * VM Args:-Xss128k * create by jokermo **/ public class JavaVMStackSOF { private int stackLength = 1; public void stackLeak(){ stackLength++; stackLeak(); } public static void main(String[] args){ JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF(); try { javaVMStackSOF.stackLeak(); }catch (Throwable e){ System.out.print("stack Length:" + javaVMStackSOF.stackLength); throw e; } } }
结果:
结果显示:在单线程情况下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。
在多线程情况下,通过不断创建线程可以产生内存溢出异常。
解决方法:
- 通过减少线程数或者更换64位的虚拟机。
- 若无法满足上面条件,可以通过减小最大堆和减少栈容量来获取更多线程。
3.方法区和运行时常量池溢出
运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。
异常信息为permGen space
4.本机直接内存溢出