堆溢出
问题一般是由于不断的创建对象,随着对象数量的增加,总容量初级到了最大堆容量限制后就会导致内存溢出异常。
1.处理方法:
通过参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/test
可以让虚拟机在出现内存溢出异常的时候,Dump出当前的内存堆转储快照以便分析。
解决这个内存区域的异常,常规的方式是通过内存映像分析工具堆Dump出来的堆转储快照进行分析。(内存映像分析工具可以使用Eclipse Memory Analyzer 下载地址:https://www.eclipse.org/mat/downloads.php)
2.确认几点:
- 1.到底是发生了内存泄漏还是内存溢出?
- 2.确认内存中导致OOM的对象是否是必要的?
如果是内存泄漏,可进一步使用工具查看泄漏对象到GC Roots的引用链,找到泄漏对象的引用路径,与那些GC Roots相关联,导致无法回收他们。
如果不是内存泄漏
1)检查Java虚拟机的堆参数(-Xmx -Xms
),看看是否有向上调整的空间。
2)检查对象生命周期是否过长,存储实际是否不合理,来减少内存消耗。
最佳实践
不使用的对象可以置为null。参照ArrayList的源码,remove()
方法中,置对象为null,clear to let GC do its work
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
3.特殊注意:
1)针对JDK8而言,增加了元空间概念,元空间在Java8中被移到Java堆中。所以一般而言,方法区中产生的OOM基本是与堆中类似。
例如Spring等一些框架中大量的使用了CGlib增加字节码,生成了大量的动态类。有时候破坏性的使用方式就会导致元空间内存即Java堆内存被占满导致内存溢出。
2)直接内存溢出
一个明显的特征是Heap Dump文件中不会有什么明显的异常情况。
通常可能引起的原因是,因为一个进程占用固定的内存大小,默认情况下,直接内存大小和堆内存大小分配相同,有时候手动分配堆内存,会压缩直接内存容量,像NIO操作会大量使用直接内存,可能导致 OOM。
虚拟机栈和本地方法栈溢出
《Java虚拟机规范》中描述了两种异常
- 线程请求的栈深度大于虚拟机所允许的最大深度,将抛出
StackOverflowError
。 - 虚拟机的栈内存允许动态扩展,当扩展容量无法申请到足够的内存,则抛出OutOfMemory。
由于HotSpot虚拟机默认不允许动态扩展,所以在日常开发中,栈空间一般不会发生OutOfMemory。
网友评论