您的位置:首页 > 编程语言 > Java开发

《深入理解java虚拟机》读书笔记——OOM发生的原因及解决方案

2017-06-24 13:26 591 查看
除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OOM的可能,如果能够区分根据报错区分出是哪些区域报出来的异常,会更便于定位问题,解决问题。

一、java堆溢出

java.lang.OutOfMemoryError:java heap space

原因:产生大量不会被垃圾回收机制清除的对象(GC Roots到对象之间有可达路径)

解决方案:先通过内存映像分析工具对Dump出来的堆转储快照进行分析,确认到底是发生了内存泄露(Memory Leak)还是内存溢出(Memory Overflow),如果是内存泄露,查看泄露对象到GC Roots的引用链,定位出泄露代码的位置。如果是内存溢出,那么就可以检查堆参数(-Xms
与 -Xmx),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况。

关于内存溢出与内存泄露的区别:

内存溢出是指申请的内存空间比剩余的内存空间要大,比如剩余1M的空间,但是实例化了一个2M的对象,就发生了内存溢出。内存泄露是指程序在申请了空间之后,无法释放已申请的空间,而这部分空间程序也访问不到,一次内存泄露可以忽视,但是内存泄露的堆积,最终也会造成内存溢出。(我的理解就是看内存中的对象是不是必要的,是必要的那就是内存溢出,不是必要的也没办法清除他的,就是内存泄露,需要找泄露的代码)

看这个例子:

public class HeapOOM {
static class OOMObject{

}

public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while(true){
list.add(new OOMObject());
}
}
}

jvm参数是:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=E:\JAVA\dump

因为堆内存就设置了20m,所以启动程序后很快能报OOM



打开jdk/bin/jvisualvm.exe,然后在这个可视化的界面中打开刚刚保存的内存堆转储快照,可以看到



HeapOOM类的内部类OOMObject的实例化对象几乎占满了整个堆内存,所以也就查到了堆内存溢出的原因,当然实际的没这么简单,这里只是简单的说明下。

,

二、虚拟机栈和本地方法栈溢出

由于Hot Spot虚拟机的实现是不区分两者的,所以可以通过-Xss参数来设定栈容量。

1、java.lang.StackOverflowError

原因:在单线程下,虚拟机栈容量太小或者栈帧太大,会抛出SO;

解决方法:增大虚拟机栈容量;看看是不是定义了大量的本地变量,进行优化;可能是递归没有出口导致方法的栈深度过大,给递归补上出口。

2、java.lang.OutOfMemoryError: unable to create new native thread

原因:在多线程下,大量创建新线程,会抛出OOM,每个线程的栈分配的内存越大,越容易产生;

解决方法:减少线程产生、减少最大堆、减少栈容量;

三、运行时常量池溢出

1、java.lang.OutOfMemoryError: PermGen space
                   at java.lang.String.intern(Native Method)

  原因:在jdk1.6及之前的版本中,代码在运行时创建了大量的常量,超出了常量池上限;

  解决方法:在jdk1.6及之前的版本中,可以通过修改-XX:PermSize和-XX:MaxPermSize参数来修改方法区大小,从而修改常量池大小;

2、java.lang.OutOfMemoryError: Java heap space
原因:在jdk1.7及以后的版本把常量池从方法区移到了堆中,代码运行时创建了大量的常量,造成了堆内存溢出

解决方法:通过修改-Xmx、-Xms来修改堆内存的大小。

四、方法区溢出

 java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass(Native Method)

  原因:在运行时,ClassLoader动态加载了大量的Class信息,超出方法区上限;
  解决方法:通过修改-XX:PermSize和-XX:MaxPermSize参数来修改方法区大小;

五、本机直接内存溢出

java.lang.OutOfMemoryError

      at sun.misc.Unsafe.allocateMemory(Native Method)

      at com.bckj.OOM.DirectMemoryOOM.main(DirectMemoryOOM.java:18)

直接内存溢出,一个明显的特征就是在Heap Dump文件中不会看见明显的异常,若发现OOM后Dump文件很小,而程序中又直接或间接的使用了NIO,就可以考虑下这个原因
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息