我认为其他人在解释为什么cnt> 0方面做得很好,但是关于为什么cnt =
4以及为什么cnt在不同设置之间变化如此之大的细节还不够多。我将在这里尝试填补这一空白。
让
- X是总堆栈大小
- M是我们第一次进入main时使用的堆栈空间
- R是每次我们进入main时增加的堆栈空间
- P是运行所需的堆栈空间
System.out.println
当我们第一次进入main时,剩下的空间是XM。每个递归调用占用R更多的内存。因此,对于1个递归调用(比原来多1个),内存使用量为M
+R。假设在C个成功的递归调用之后抛出StackOverflowError,即M + C * R <= X和M + C *(R +
1)>X。在出现第一个StackOverflowError时,还剩下X-M-C * R内存。
为了能够运行
System.out.prinln,我们需要在堆栈上保留P的空间。如果碰巧X-M-C * R> =
P,则将打印0。如果P需要更多空间,则我们从堆栈中删除帧,以cnt ++为代价获得R内存。
当
println是最后能够运行,X - M - (C - CNT)* R> = P。因此,如果P是大对于特定系统,那么CNT将是大的。
让我们用一些例子来看一下。
示例1: 假设
- X = 100
- M = 1
- R = 2
- P = 1
然后C = floor((XM)/ R)= 49,cnt = ceiling((P-(X-M-C * R))/ R)= 0。
示例2: 假设
- X = 100
- M = 1
- R = 5
- P = 12
那么C = 19,cnt = 2。
示例3: 假设
- X = 101
- M = 1
- R = 5
- P = 12
那么C = 20,cnt = 3。
示例4: 假设
- X = 101
- M = 2
- R = 5
- P = 12
那么C = 19,cnt = 2。
因此,我们看到系统(M,R和P)和堆栈大小(X)都会影响cnt。
附带说明,
catch开始需要多少空间并不重要。只要没有足够的空间容纳
catch,那么cnt就不会增加,因此不会有外部影响。
编辑
我收回我所说的话
catch。它确实发挥了作用。假设它需要T数量的空间才能启动。当剩余空间大于T时,cnt开始增加,而
println当剩余空间大于T
+ P 时,cnt开始运行。这为计算增加了一个额外的步骤,并进一步混淆了已经很混乱的分析。
编辑
我终于有时间进行一些实验以支持我的理论。不幸的是,该理论似乎与实验不符。实际发生的情况非常不同。
实验设置:具有默认java和default-jdk的Ubuntu 12.04服务器。Xss从70,000开始(以1字节为增量)到460,000。
结果位于:https
:
//www.google.com/fusiontables/DataSource?docid=1xkJhd4s8biLghe6gZbcfUs3vT5MpS_OnscjWDbM
我创建了另一个版本,其中删除了每个重复的数据点。换句话说,仅显示与先前不同的点。这使得更容易看到异常。https://www.google.com/fusiontables/DataSource?docid=1XG_SRzrrNasepwZoNHqEAKuZlHiAm9vbEdwfsUA



