在Java虚拟机规范描述中,出程序计数器外,其他几个运行时区域都有可能发生OutOfMemoryError异常。接下来将对各区域分别进行分析介绍,内容包括触发各区域OutOfMemoryError异常地代码,以及对其进行排查判定的过程。还会初步涉及几个与内存相关的最基本虚拟机参数。
1、Java堆溢出
Java堆用于存储对象实例,只要不断创建对象并保证对象不会被回收,那么当对象数量到达最大堆的容量限制后就会产生堆内存溢出异常。
测试代码如下:
/** * 探究Java堆溢出 * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * 将堆最小值(-Xms)与堆最大值(-Xmx)参数设置为一样(20MB),可避免堆自动扩展 * * -XX:+HeapDumpOnOutOfMemoryError :让虚拟机在出现内存溢出异常时可Dump出 * 当前的内存堆转储快照以便时候进行分析 * @author yangtf * */ 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
Dumping heap to java_pid18152.hprof ...
Heap dump file created [28008150 bytes in 0.132 secs]
产生上述异常后,异常信息:java.lang.OutOfMemoryError: Java heap space 。
并且会在代码根路径下生成一个 java_pid18152.hprof 文件,此文件的生成是由于-XX:+HeapDumpOnOutOfMemoryError 配置的结果。
要解决这个区域的异常,一般是通过内存映像分析工具(如 Memory Analyzer)对Dump出来的堆转储快照进行分析,重点是确认出现了内存泄漏还是内存溢出。
内存泄漏:对象已经死了,无法通过垃圾收集器进行自动回收,通过找出泄露的代码位置和原因,才好确定解决方案
内存溢出:内存中的对象都还必须存活着,这说明Java堆分配空间不足,检查堆设置大小(-Xmx与-Xms),检查代码是否存在对象生命周期太长、持有状态时间过长的情况
下面介绍用 Memory Analyzer (以下简称MAT ~Tool)进行分析的过程
1)下载与安装:下载链接点击上面 Memory Analyzer。
2)打开分析文件:在根目录下查找 java_pid18152.hprof 文件,并打开。
3)打开后见下图:
从上图可以看到它的大部分功能。
1. Histogram可以列出内存中的对象,对象的个数以及大小。
2. Dominator Tree可以列出那个线程,以及线程下面的那些对象占用的空间。
3.Top consumers通过图形列出最大的object。
4.Leak Suspects通过MA自动分析泄漏的原因
Retained size:是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,retained size是该对象被GC之后所能回收到内存的总和。
可以看出Object类、Thread类、HeapOOM类占用了绝大部分空间
Dominator Tree如下图:
可以看出 Thread 线程占用了很大空间
点击上图中 【Details】
可以看出积累了过多的 OOMObject ,选择一个左击==>List objects==>with outgoing rederences,查看Thread都应用了什么对象。
现在看到引用的对象:
可以看到对象是引起问题的关键,在对象上右击==>Path to GC Roots==>exclude weak reference
从下图看出在 java.lang.Thread 中保存了 OOMObject的引用。所以可以得出结论是由于运行时持有大量的 OOMObject 引起了内存泄漏。
以上是通过MAT分析测试程序查找内存溢出的原因。
堆溢出解决方式:
内存泄漏:掌握内存泄露对象信息及GC Roots引用链信息,就可以比较准确地定位出泄漏代码的位置。
内存溢出:检查虚拟机的堆参数(-Xmx与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过程、持有状态时间过长的情况,尝试减少程序运行期间的内存消耗。