在JVM规范中,除了程序计数器,虚拟机内存的其他几个运行区域都有可能发生OutOfMemoryError异常。
Java堆溢出:
Java堆是用来存储对象实例,只要不停地创建对象实例,并且让GC ROOTS到对象之间有可达路径来避免垃圾回收机制清除这些对象,当对象数量达到最大堆的容量限制就会产生内存溢出的异常。
通过设置JVM参数-XX:+HeapDumpOnOutOfMemoryError可以让JVM在发生内存泄露异常Dump出当前内存堆转储快照方便后面分析。默认情况下,堆内存快照会保存在JVM的启动目录下名为java_pid<pid>.hprof 的文件里(在这里<pid>就是JVM进程的进程号)
当堆内存溢出时,会在异常信息后提示Java heap space
使用内存映像分析工具进行分析,判断是内存溢出还是内存泄露
1、内存泄露:
通过工具查看泄露对象到GC ROOTS引用链。就可以找到泄露对象是通过怎样的路径与GC ROOTS相关联,导致垃圾收集器无法自动回收他们
2、内存溢出:
检查虚拟机的堆参数,看是否还可以调大,从代码上检查某些对象生命周期过长,持有时间过长的情况。
例子:
使用JProfiler
1、打开快照文件
2、查看占用内存较大的对象
2、查看该对象通过怎样的路径与gc roots相关联
虚拟机栈和本地方法栈溢出
在HotSpot中是不区分虚拟机栈和本地方法栈的,虚拟机在扩展栈时无法分配到足够的内存空间就会抛出OutOfMemoryError。
方法区和运行时常量池溢出
运行时常量池溢出,错误后紧跟PermGen space说明运行时常量池属于方法区的一部分。
运行时产生大量的类可能填满方法区,如一些框架使用CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载进内存
本机直接内存溢出
在Heap Dump中不会出现明显的异常,可以考虑是这个情况DirectByteBuffer直接通过反射获取Unsafe实例进行内存分配,会抛出内存溢出异常,但是他抛出的异常没有真正向操作系统申请分配内存,而是通过计算得知内存无法分配。