堆栈消费的变化来自何处?

在从这个问题运行测试代码并摆弄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); } } } 

Silly 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 

如果它与HotSpot编译器启动的时间有关,我不会感到惊讶,特别是如果你在多核系统上运行的话。

编辑:要检查这一点,您可以使用-Xint启动测试。 如果您开始获得可重现的结果,那么非确定性行为可能是由HotSpot编译器引起的。

我没有观察到这种行为。 在我的环境中(Windows 7 x64,1.6.0_19 x86 JDK),它们始终一直失败到某个点,然后开始在ss = 1400处传递(由于它们全部通过了您的参数,我将其降低了)。

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

你确定它实际上是你正在捕获的StackOverflowError吗? 我会单独捕获该错误,而不是通用的Throwable,以确保它不是其他东西,如OutOfMemoryError。

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

这样它就会打印一个堆栈跟踪,如果它实际上并不是你得到的StackOverflowError。