几种可能导致OOM异常的情况

本文将讲述两种可能导致OOM的案例
注意:1、程序计数器不会发生OOM
2、在jdk1.8中已经取消了永久代概念,改由元空间取代,就算设置-XX:MetaspaceSize=1m;这种参数限制大小,实际操作时并没有起到多大用处,因此很难通过简单的demo复现以前老年代产生OOM的异常。

由于递归深度过长导致
jvm对递归深度有限制,具体深度由于jdk 版本等的不同有差异,下面这个案例使用jdk1.8测试,递归深度测试为7893,附测试案例:

static int stackDeep = 0;
public static void main(String[] args) {
try{
TestStackOverFlow testStakOverFlow = new TestStackOverFlow();
testStakOverFlow.foo();
}catch(Throwable t){
System.out.println(t);
System.out.println(“栈的深度为:” + stackDeep);
}
}

public void foo() {
stackDeep ++ ;
foo();
}

测试结果:

java.lang.StackOverflowError
栈的深度为:7893

堆溢出
运行时堆溢出是一种常见的溢出情况,下面展示其中的一种,有感兴趣的朋友可以使用Set来代替List重写该案例,异常堆栈信息将不同:
设置JVM参数:

-Xmx1m -XX:+PrintGCDetails

测试代码:

int i = 0;
while(true){
test.add(String.valueOf(i++).intern());
}

异常日志打印如下:

[Full GC (Allocation Failure) [Tenured: 1407K->1408K(1408K), 0.0080421 secs] 1855K->1820K(1984K), [Metaspace: 82K->82K(4480K)], 0.0080726 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)…

可以看到,*后一次GC,是full GC,异常报错在扩容的地方,由于空间不够,扩容时申请不到空间,引发OOM。

小结:OOM发生的情况很多,很多时候需要结合实际产生的问题来具体分析,比如:机器实际内存2g,设置堆大小:-Xmx与-Xms为1.8g,而使用NIO的情况很多,那么可能由于直接内存不够导致异常;另外大对象的使用不当也可能导致OOM;再比如,大量数据直接使用内存作为缓存,也可能导致OOM…很多时候,可以根据JVM参数(比如设置堆栈等的大小,GC收集器等)或者增加机器配置来进行调优。实际上,很多OOM,都是程序不当引起的,因此出现该情况,应首先考虑程序问题。