jvm_内存溢出_虚拟机栈和本地方法栈溢出

关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

在实验中,如果将实验范围限制于单线程中的操作,尝试了下面两种方法均无法让虚拟机产生OutOfMemoryError异常,尝试的结果都是获得StackOverflowError异常。

  1. 使用-Xss参数减少栈内存容量,结果:抛出StackOverflowError异常,异常出现时输出的栈深度相应缩小。
    首先设置栈容量参数为-Xss128K
    代码:
package jvm;

/**
 * -Xss128K
 * 
 * @author Poison
 *
 */
public class JavaVMStackSOF {
    private int stackLength = 1;

    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try {
            oom.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:" + oom.stackLength);
            throw e;
        }
    }
}

运行结果:
stack length:995
Exception in thread “main” java.lang.StackOverflowError
at jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
at jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
……

  1. 定义了大量的本地变量,增加此方法帧中本地变量表的长度。结果:抛出StackoverflowError异常时输出的栈深度相应缩小。
    代码:
package jvm;

/**
 * -Xss128K
 * 
 * @author Poison
 *
 */
public class JavaVMStackSOF {
    private int stackLength = 1;

    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        long e0 = 1;
        long e1 = 1;
        long e2 = 1;
        long e3 = 1;
        long e4 = 1;
        long e5 = 1;
        long e6 = 1;
        long e7 = 1;
        long e8 = 1;
        long e9 = 1;
        long q0 = 1;
        long q1 = 1;
        long q2 = 1;
        long q3 = 1;
        long q4 = 1;
        long q5 = 1;
        long q6 = 1;
        long q7 = 1;
        long q8 = 1;
        long q9 = 1;
        long r0 = 1;
        long r1 = 1;
        long r2 = 1;
        long r3 = 1;
        long r4 = 1;
        long r5 = 1;
        long r6 = 1;
        long r7 = 1;
        long r8 = 1;
        long r9 = 1;
        long t0 = 1;
        long t1 = 1;
        long t2 = 1;
        long t3 = 1;
        long t4 = 1;
        long t5 = 1;
        long t6 = 1;
        long t7 = 1;
        long t8 = 1;
        long t9 = 1;
        long y0 = 1;
        long y1 = 1;
        long y2 = 1;
        long y3 = 1;
        long y4 = 1;
        long y5 = 1;
        long y6 = 1;
        long y7 = 1;
        long y8 = 1;
        long y9 = 1;
        long u0 = 1;
        long u1 = 1;
        long u2 = 1;
        long u3 = 1;
        long u4 = 1;
        long u5 = 1;
        long u6 = 1;
        long u7 = 1;
        long u8 = 1;
        long u9 = 1;
        long i0 = 1;
        long i1 = 1;
        long i2 = 1;
        long i3 = 1;
        long i4 = 1;
        long i5 = 1;
        long i6 = 1;
        long i7 = 1;
        long i8 = 1;
        long i9 = 1;
        long o0 = 1;
        long o1 = 1;
        long o2 = 1;
        long o3 = 1;
        long o4 = 1;
        long o5 = 1;
        long o6 = 1;
        long o7 = 1;
        long o8 = 1;
        long o9 = 1;
        long p0 = 1;
        long p1 = 1;
        long p2 = 1;
        long p3 = 1;
        long p4 = 1;
        long p5 = 1;
        long p6 = 1;
        long p7 = 1;
        long p8 = 1;
        long p9 = 1;
        long a0 = 1;
        long a1 = 1;
        long a2 = 1;
        long a3 = 1;
        long a4 = 1;
        long a5 = 1;
        long a6 = 1;
        long a7 = 1;
        long a8 = 1;
        long a9 = 1;
        long s0 = 1;
        long s1 = 1;
        long s2 = 1;
        long s3 = 1;
        long s4 = 1;
        long s5 = 1;
        long s6 = 1;
        long s7 = 1;
        long s8 = 1;
        long s9 = 1;
        long d0 = 1;
        long d1 = 1;
        long d2 = 1;
        long d3 = 1;
        long d4 = 1;
        long d5 = 1;
        long d6 = 1;
        long d7 = 1;
        long d8 = 1;
        long d9 = 1;
        long f0 = 1;
        long f1 = 1;
        long f2 = 1;
        long f3 = 1;
        long f4 = 1;
        long f5 = 1;
        long f6 = 1;
        long f7 = 1;
        long f8 = 1;
        long f9 = 1;
        long g0 = 1;
        long g1 = 1;
        long g2 = 1;
        long g3 = 1;
        long g4 = 1;
        long g5 = 1;
        long g6 = 1;
        long g7 = 1;
        long g8 = 1;
        long g9 = 1;
        long h0 = 1;
        long h1 = 1;
        long h2 = 1;
        long h3 = 1;
        long h4 = 1;
        long h5 = 1;
        long h6 = 1;
        long h7 = 1;
        long h8 = 1;
        long h9 = 1;
        long j0 = 1;
        long j1 = 1;
        long j2 = 1;
        long j3 = 1;
        long j4 = 1;
        long j5 = 1;
        long j6 = 1;
        long j7 = 1;
        long j8 = 1;
        long j9 = 1;
        long k0 = 1;
        long k1 = 1;
        long k2 = 1;
        long k3 = 1;
        long k4 = 1;
        long k5 = 1;
        long k6 = 1;
        long k7 = 1;
        long k8 = 1;
        long k9 = 1;
        long l0 = 1;
        long l1 = 1;
        long l2 = 1;
        long l3 = 1;
        long l4 = 1;
        long l5 = 1;
        long l6 = 1;
        long l7 = 1;
        long l8 = 1;
        long l9 = 1;
        long z0 = 1;
        long z1 = 1;
        long z2 = 1;
        long z3 = 1;
        long z4 = 1;
        long z5 = 1;
        long z6 = 1;
        long z7 = 1;
        long z8 = 1;
        long z9 = 1;
        long c0 = 1;
        long c1 = 1;
        long c2 = 1;
        long c3 = 1;
        long c4 = 1;
        long c5 = 1;
        long c6 = 1;
        long c7 = 1;
        long c8 = 1;
        long c9 = 1;
        long v0 = 1;
        long v1 = 1;
        long v2 = 1;
        long v3 = 1;
        long v4 = 1;
        long v5 = 1;
        long v6 = 1;
        long v7 = 1;
        long v8 = 1;
        long v9 = 1;
        long b0 = 1;
        long b1 = 1;
        long b2 = 1;
        long b3 = 1;
        long b4 = 1;
        long b5 = 1;
        long b6 = 1;
        long b7 = 1;
        long b8 = 1;
        long b9 = 1;
        long n0 = 1;
        long n1 = 1;
        long n2 = 1;
        long n3 = 1;
        long n4 = 1;
        long n5 = 1;
        long n6 = 1;
        long n7 = 1;
        long n8 = 1;
        long n9 = 1;
        long m0 = 1;
        long m1 = 1;
        long m2 = 1;
        long m3 = 1;
        long m4 = 1;
        long m5 = 1;
        long m6 = 1;
        long m7 = 1;
        long m8 = 1;
        long m9 = 1;
        long qq0 = 1;
        long qq1 = 1;
        long qq2 = 1;
        long qq3 = 1;
        long qq4 = 1;
        long qq5 = 1;
        long qq6 = 1;
        long qq7 = 1;
        long qq8 = 1;
        long qq9 = 1;
        long ww0 = 1;
        long ww1 = 1;
        long ww2 = 1;
        long ww3 = 1;
        long ww4 = 1;
        long ww5 = 1;
        long ww6 = 1;
        long ww7 = 1;
        long ww8 = 1;
        long ww9 = 1;
        try {
            oom.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:" + oom.stackLength);
            throw e;
        }
    }
}

运行结果:
stack length:938
Exception in thread “main” java.lang.StackOverflowError
at jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
at jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
……

实验结果表明:在单个线程下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackoverflowError异常。
注:Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

如果测试时不限于单线程,通过不断地建立线程的方式倒是可以产生内存溢出异常,但是,这样产生的内存溢出异常与栈空间是否足够大并不存在任何联系,或者准确地说,在这种情况下,给每个线程的栈分配的内存越大,反而越容易产生内存溢出异常。
原因其实不难理解,操作系统分配给每个进程的内存是有限制的,譬如32位的Windows限制为2GB。虚拟机提供了参数来控Java堆和方法区这两部分内存的最大值。剩余的内存为2GB(操作系统限制)减去Xmx(最大堆容量),再减去MaxPermSize(最大方法区容量),程序计数器消耗内存很小,可以忽略掉。如果虚拟机进程本身耗费的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈“瓜分”了。每个线程分配到的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽。
例:

package jvm;

/**
 * -Xss2M
 * 
 * @author Poison
 *
 */
public class JavaVMStackOOM {
    private void dontStop() {
        while(true){
        }
    }

    public void stackLeakByThread() {
        while (true) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    dontStop();
                }
            });
            thread.start();
        }
    }

    public static void main(String[] args) {
        JavaVMStackOOM oom = new JavaVMStackOOM();
        oom.stackLeakByThread();
    }
}

注:在Windows平台的虚拟机中,Java的线程是映射到操作系统的内核线程上的,所以上述代码执行时有较大的风险,会使CPU利用率接近100%而导致系统假死。
书上给的运行结果为:
Exception in thread “main” java.lang.OutOfMemoryError: unable to create new native thread

我在测试中未等到出现异常信息机器就近乎卡死了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值