在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(下面都叫OOM)异常的肯能,下面就通过一些实例来验证异常发生的场景。 在下面举的例子中,在注释中会设定一些虚拟机的参数,在”VM Args”后边,如果你是用控制台命令来执行的程序,直接把这些参数放到Java命令之后就行,如果用的开发工具,Eclipse IDE,才Debug/Run页签中设置。
Java堆溢出
堆是用来存储对象实例的,当我们不断的创建对象,并且保证GC Roots和对象之间有相互的引用关系(GC Roots指垃圾回收器的对象,GC会手机那些不是GC Roots且没有被GC Roots引用的对象),当对象数量达到最大堆的容量的时候,就会产生内存溢出异常。
/**
* VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
* @author Administrator
*
*/
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堆的大小为20MB,不可扩展(将堆的最小值-Xms 参数与最大值-Xmx参数设置为一样就可以避免堆自动扩展),通过 -XX:+HeapDumpOnOutOfMemoryError 当虚拟机出现内存溢出的时候Dump出当前的内存堆转储快照以便后边进行分析。运行结果如下:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid5504.hprof ...
Heap dump file created [27872304 bytes in 0.150 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2245)
at java.util.Arrays.copyOf(Arrays.java:2219)
at java.util.ArrayList.grow(ArrayList.java:242)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
at java.util.ArrayList.add(ArrayList.java:440)
at test.HeapOOM.main(HeapOOM.java:20)
刷新项目你就可以看到快照文件,可以用Eclipse Memory Analyzer来打开并进行分析。确定是Memory Leak,还是Memory Overflow。 处理思路:如果是内存泄漏,通过工具进一步查看泄漏对象到GC Roots的引用链,找到泄漏对象是如何和GC Roots相关联并导致垃圾回收器无法自动回收这些对象的。后边就可以判断产生内存溢出的代码的位置。 如果不存在内存泄漏问题,检查虚拟机的堆参数(-Xms -Xmx)跟物理机器对比是否还可以调大,在代码层面上看看是否存在某些对象生命周期过长、持有状态时间过长的情况。减少程序运行期间的内存消耗。