代码之家  ›  专栏  ›  技术社区  ›  David J. Liszewski

堆栈消耗的可变性来自哪里?

  •  1
  • David J. Liszewski  · 技术社区  · 15 年前

    运行测试代码时 this question 再摆弄JVM的线程堆栈大小,我发现结果不一定是可重复的:有一些堆栈大小的值,程序会为这些值设置 有时 java.lang.StackOverflowError

    我的问题是:“是什么导致堆栈空间消耗的变化?”

    另外,中断堆栈是否可以放在这个程序的主线程上?对于其他JVM实现和/或操作系统,结果是否同样不确定?

    测试代码

    public class PointlessRecursion {
    
        private static final long N = 1 << 15;
    
        private static long addOne(long n) {
            return (n < 2) ? n : 1 + (addOne(n - 1));
        }
    
        public static void main(String[] args) {
            try {
                long x = addOne(N);
                System.out.println(x);
                assert(x == N);
                System.exit(0);
            } catch (StackOverflowError e) {
                System.exit(1);
            } catch (Throwable t) {
                System.err.println(t.toString());
                System.exit(2);
            }
        }
    }
    

    为每个堆栈大小设置多次运行测试程序的愚蠢bash脚本

    #! /bin/bash
    s=2000
    while [ $s -lt 4100 ] ; do
        i=0
        pass=0
        fail=0
        while [ $i -lt 10 ] ; do
            java -Xss${s}k -cp ~/bin/classes PointlessRecursion > /dev/null
            if [ $? -eq 0 ] ; then
                pass=$((pass+1))
            elif [ $? -eq 1 ] ; then
                fail=$((fail+1))
            fi
            i=$((i+1))
        done
        echo ss=$s pass=$pass fail=$fail
        s=$(($s+100))
    done
    

    结果

    $ java -version
    java version "1.6.0_20"
    Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
    Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode)
    $ ~/bin/stack-test.sh
    ss=2000 pass=0 fail=10
    ss=2100 pass=1 fail=9
    ss=2200 pass=0 fail=10
    ss=2300 pass=2 fail=8
    ss=2400 pass=1 fail=9
    ss=2500 pass=1 fail=9
    ss=2600 pass=2 fail=8
    ss=2700 pass=6 fail=4
    ss=2800 pass=3 fail=7
    ss=2900 pass=1 fail=9
    ss=3000 pass=3 fail=7
    ss=3100 pass=3 fail=7
    ss=3200 pass=6 fail=4
    ss=3300 pass=2 fail=8
    ss=3400 pass=4 fail=6
    ss=3500 pass=10 fail=0
    ss=3600 pass=9 fail=1
    ss=3700 pass=10 fail=0
    ss=3800 pass=10 fail=0
    ss=3900 pass=10 fail=0
    ss=4000 pass=10 fail=0
    
    2 回复  |  直到 9 年前
        1
  •  3
  •   gpeche    15 年前

    编辑: 要检查这一点,您可以使用 -Xint . 如果您开始得到可再现的结果,那么不确定的行为可能是由热点编译器引起的。

        2
  •  1
  •   Mark Peters    15 年前

    我唯一更改的是没有指定类路径(我从与类文件相同的目录运行脚本)。但即使在指定类路径时,我也得到了相同的结果。

    //...
    } catch (StackOverflowError e) {
        System.exit(1);
    }
    

    这样它会打印一个stacktrace,如果它实际上不是一个stackoverflower错误。

    推荐文章