java执行内存溢出_Java内存溢出情况

在Java运行时数据区中,除了Program Counter Register(程序计数器)之外,其他几个数据区中均可能发生OutOfMemoryError,俗称OOM。

1.Java堆溢出

堆主要是用于对象创建时内存的分配,只要我们不断创建对象,并且这些对象在GC时不会被回收掉,则会发生Java堆内存溢出。我们通过以下程序来模拟Java堆溢出的情况:

首先配置虚拟机参数,将堆大小设置为20M并且不可扩展:

-verbose:gc -Xms20M -Xmx20M -XX:+PrintGCDetails

public class Test {

public static class Obj {

}

public static void main(String[] args) throws Exception {

List list = new ArrayList();

while(true) {

//循环创建对象,并且对象并不能被回收

list.add(new Obj());

}

}

}

运行结果如下:

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.main(Test.java:13)

Java堆内存溢出是最常见的内存溢出情况,出现Java堆的OOM时,异常栈信息一般会有java.lang.OutOfMemoryError: Java heap space。

2.虚拟机栈溢出

当线程执行一个方法时,java虚拟机会创建一个新的栈帧(stack frame),并压入到该线程的栈中,只要这个方法没有返回,这个栈帧就一直存在于栈中。如果方法嵌套调用层次太多(如上面的递归调用),则该线程栈中的栈帧会越来越多,并且方法都没有结束返回,这些栈帧也不会出栈,最终导致这个线程的栈中所有栈帧的大小总和大于-Xss设置的值,从而产生StackOverflowError异常。

本地方法栈与虚拟机栈类似,在这里会有2种溢出情况:

如果线程请求的栈深度大于虚拟机所允许的最大深度,则抛出StackOverflowError异常;

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

我们来模拟栈溢出情况,首先配置虚拟机参数,设置栈大小为160k:

-verbose:gc -Xss160k -XX:+PrintGCDetails

public class Test {

private int l = 1;

public void callMethod() {

System.out.println(l);

l++;

callMethod();

}

public static void main(String[] args) throws Exception {

Test t = new Test();

try {

t.callMethod();

} catch(Exception e) {

System.out.println("stack length : " + t.l);

throw e;

}

}

}

运行结果如下:

Exception in thread "main" java.lang.StackOverflowError

at Test.callMethod(Test.java:11)

at Test.callMethod(Test.java:12)

at Test.callMethod(Test.java:12)

......

......

在单线程下测试以上代码时,发现只会抛出StackOverflowError异常。

3.方法区的溢出

方法区用于存放Class信息、常量等,方法区的内存溢出主要有2种情况:

现在的很多主流框架如Spring,会在运行时生成大量的增强类,增强的类越多,就需要越大的方法区来保存动态生成的Class信息,因此也越容易发生内存溢出。

运行时常量池内存溢出。

我们可以使用String.intern()方法不断生成字符串常量来模拟内存溢出,同样先设置方法区的内存大小。

-verbose:gc -XX:PermSize=4M -XX:MaxPermSize=4M -XX:+PrintGCDetails

public class Test {

public static void main(String[] args) {

List list = new ArrayList();

int i = 0;

while(true) {

System.out.println(i);

list.add(String.valueOf(i++).intern());

}

}

}

运行结果如下:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

at java.lang.String.intern(Native Method)

......

从异常栈信息中可以看到,在java.lang.OutOfMemoryError后面跟随的是"PermGen space",这里说明常量池是属于虚拟机中永久代的一部分。

4.小结

本文主要模拟了不同的内存区域出现内存溢出的情况,当我们自己的应用程序出现内存溢出时,就能根据异常信息定位到内存溢出的代码所在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值