-
找到内存溢出的原因
-
堆溢出
- 异常说明:因为大量对象都直接分配在堆上,绝大部分的内存溢出都属于这种情况;
- 原因分析:因为大量对象占据了堆空间,而这些对象都持有强引用,导致无法回收,当大于Xmx参数指定的堆空间大小时,就会抛出溢出错误;
- 示例1:
-
public class SimpleHeapOOM { public static void main(String[] args) { ArrayList<byte[]> list = new ArrayList<byte[]>(); for (int i = 0; i < 10240; i++) { list.add(new byte[1024*1024]); } } } | 错误提示: Java heap space表示堆空间溢出 |
-
直接内存溢出
- 关于直接内存看这里:直接内存
- 原因分析:直接内存没有被Java虚拟机完全托管,若使用不当,也容易触发直接内存溢出,导致宕机;
- 异常说明:直接内存溢出,一般抛出OutOfMemoryError错误。直接内存不一定能够触发GC(触发直接内存使用量达到了-XX:MaxDirectMemorySize的设置),所以它产生的没有被任何对象所引用的变量可能并不会被回收;
- 解决方案:合理的使用显示GC(也就是System.gc()),或者设定一个系统实际可达的-XX:MaxDirectMemorySize值;
-
过多线程导致OOM
- 原因分析:每个线程的开启都要占用系统内存,线程的栈空间也是在堆外分配的,因此和直接内存非常相似;
- 异常说明:此异常一般为"unable to create new native thread",表示系统创建线程的数量已经饱和,原因是Java进程已经达到了可使用的内存上限;
-
解决方案:
- 可以尝试减少堆空间,也就是减少运行参数 -Xmx的值;
-
减少每个线程所占的内存空间,-Xss参数可以指定线程的栈空间;
- 减少线程的栈空间大小,栈溢出的风险会响应上升;
-
永久区溢出
- 原因分析:永久区用来存放类元数据。如果一个系统定了太多的类型,永久区是有可能溢出的。JDK1.8中,永久区被元数据区域替代,但功能类似;
- 异常说明:异常信息为"java.lang.OutOfMemoryError:PermGen space"
-
解决方案:
- 增加MaxPermSize的值;
- 减少系统需要的类的数量;
- 使用ClassLoader合理地装载各个类,并定期进行回收;
-
GC效率低下引起的OOM
-
原因分析:GC是内存回收的关键,如果GC效率低下,那么系统的性能会受到严重的影响。如果系统的堆空间太小,那么GC所占的时间就会较多,并且回收所释放的内存就会较少。根据GC占用的系统时间,以及释放内存的大小,虚拟机会评估GC的效率,一旦虚拟机认为GC的效率过低,就有可能直接抛出OOM异常;
-
一般情况下,虚拟机会根据以下几种情况来判定是否抛出此异常:
- 花在GC上的时间是否超过了98%;
- 老年代释放的内存是否小于2%;
- eden区释放的内存是否小于2%;
- 最近是否连续5次GC都出现了上述几种情况(注意是同时出现,不是出现一个!);
- 当满足以上条件,虚拟机会抛出:GC overhead limit exceeded异常
-
- -XX:-UseGCOverheadLimit:来禁止这种OOM的产生;
-