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

来源:互联网 发布:手机实用小五金软件 编辑:程序博客网 时间:2024/06/06 00:41

关于虚拟机栈和本地方法栈,在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

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

0 0
原创粉丝点击