为什么这种方法打印4? [英] Why does this method print 4?

查看:93
本文介绍了为什么这种方法打印4?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道当您尝试捕获StackOverflowError时会发生什么,并提出以下方法:

I was wondering what happens when you try to catch an StackOverflowError and came up with the following method:

class RandomNumberGenerator {

    static int cnt = 0;

    public static void main(String[] args) {
        try {
            main(args);
        } catch (StackOverflowError ignore) {
            System.out.println(cnt++);
        }
    }
}

现在我的问题:

为什么这个方法打印'4'?

Why does this method print '4'?

我想也许是因为系统.out.println()在调用堆栈上需要3个段,但我不知道3号来自哪里。当你查看 System.out.println()的源代码(和字节码)时,它通常会导致比3更多的方法调用(因此3个段调用堆栈是不够的)。如果是因为Hotspot VM的优化应用(方法内联),我想知道另一个VM上的结果是否会有所不同。

I thought maybe it was because System.out.println() needs 3 segments on the call stack, but I don't know where the number 3 comes from. When you look at the source code (and bytecode) of System.out.println(), it normally would lead to far more method invocations than 3 (so 3 segments on the call stack would not be sufficient). If it's because of optimizations the Hotspot VM applies (method inlining), I wonder if the result would be different on another VM.

编辑

由于输出似乎是高度JVM特定的,我使用

Java(TM)SE运行时环境得到结果4(版本1.6.0_41 -b02)

Java HotSpot(TM)64位服务器VM(版本20.14-b01,混合模式)

As the output seems to be highly JVM specific, I get the result 4 using
Java(TM) SE Runtime Environment (build 1.6.0_41-b02)
Java HotSpot(TM) 64-Bit Server VM (build 20.14-b01, mixed mode)


解释为什么我认为这个问题与理解Java堆栈

我的问题不是为什么有一个cnt> 0(显然是因为 System.out .println()需要堆栈大小并在打印内容之前抛出另一个 StackOverflowError ,但为什么它具有特定值4,分别为0, 3,8,55或其他系统上的其他东西。

My question is not about why there is a cnt > 0 (obviously because System.out.println() requires stack size and throws another StackOverflowError before something gets printed), but why it has the particular value of 4, respectively 0,3,8,55 or something else on other systems.

推荐答案

我认为其他rs在解释为什么cnt> 0方面做得很好,但是关于为什么cnt = 4没有足够的细节,以及为什么cnt在不同的设置中变化如此之大。我将尝试填补此空白。

I think the others have done a good job at explaining why cnt > 0, but there's not enough details regarding why cnt = 4, and why cnt varies so widely among different settings. I will attempt to fill that void here.


  • X是总堆栈大小

  • M是第一次进入main时使用的堆栈空间

  • R是每次进入main时堆栈空间增加

  • P是运行 System.out.println

  • X be the total stack size
  • M be the stack space used when we enter main the first time
  • R be the stack space increase each time we enter into main
  • P be the stack space necessary to run 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内存。

When we first get into main, the space left over is X-M. Each recursive call takes up R more memory. So for 1 recursive call (1 more than original), the memory use is M + R. Suppose that StackOverflowError is thrown after C successful recursive calls, that is, M + C * R <= X and M + C * (R + 1) > X. At the time of the first StackOverflowError, there's X - M - C * R memory left.

能够运行 System.out.prinln ,我们需要在堆栈上留下P空间。如果发生X-M-C * R> = P,那么将打印0。如果P需要更多空间,那么我们从堆栈中删除帧,以cnt ++为代价获得R内存。

To be able to run System.out.prinln, we need P amount of space left on the stack. If it so happens that X - M - C * R >= P, then 0 will be printed. If P requires more space, then we remove frames from the stack, gaining R memory at the cost of cnt++.

println 终于能够运行时,X - M - (C - cnt)* R> = P那么如果P对于特定系统来说很大,那么cnt就会很大。

When println is finally able to run, X - M - (C - cnt) * R >= P. So if P is large for a particular system, then cnt will be large.

让我们看一下这个例子。

Let's look at this with some examples.

示例1:假设


  • X = 100

  • M = 1

  • R = 2

  • P = 1

  • X = 100
  • M = 1
  • R = 2
  • P = 1

然后C = floor((XM)/ R)= 49,cnt = ceiling((P - (X - M - C * R))/ R )= 0。

Then C = floor((X-M)/R) = 49, and cnt = ceiling((P - (X - M - C*R))/R) = 0.

示例2:假设


  • X = 100

  • M = 1

  • R = 5

  • P = 12

  • X = 100
  • M = 1
  • R = 5
  • P = 12

然后C = 19,cnt = 2.

Then C = 19, and cnt = 2.

示例3:假设


  • X = 101

  • M = 1

  • R = 5

  • P = 12

  • X = 101
  • M = 1
  • R = 5
  • P = 12

然后C = 20,cnt = 3.

Then C = 20, and cnt = 3.

示例4:假设


  • X = 101

  • M = 2

  • R = 5

  • P = 12

  • X = 101
  • M = 2
  • R = 5
  • P = 12

然后C = 19,cnt = 2.

Then C = 19, and cnt = 2.

因此,我们看到系统(M,R和P)和堆栈大小(X)都影响cnt 。

Thus, we see that both the system (M, R, and P) and the stack size (X) affects cnt.

作为旁注,无论多少空间 catch 需要启动。只要 catch 没有足够的空间,那么cnt就不会增加,所以没有外部效果。

As a side note, it does not matter how much space catch requires to start. As long as there is not enough space for catch, then cnt will not increase, so there are no external effects.

编辑

我收回了我所说的 catch 。它确实发挥了作用。假设它需要T量的空间来启动。当剩余空间大于T时,cnt开始递增,当剩余空间大于T + P时, println 运行。这为计算增加了一个额外的步骤。混淆了已经很混乱的分析。

I take back what I said about catch. It does play a role. Suppose it requires T amount of space to start. cnt starts to increment when the leftover space is greater than T, and println runs when the leftover space is greater than T + P. This adds an extra step to the calculations and further muddies up the already muddy analysis.

编辑

我终于找到时间了进行一些实验来支持我的理论。不幸的是,该理论似乎与实验不符。实际发生的情况非常不同。

I finally found time to run some experiments to back up my theory. Unfortunately, the theory doesn't seem to match up with the experiments. What actually happens is very different.

实验设置:
Ubuntu 12.04服务器,默认为java,默认为jdk。 Xss从70,000开始,以1字节为增量增加到460,000。

Experiment setup: Ubuntu 12.04 server with default java and default-jdk. Xss starting at 70,000 at 1 byte increments to 460,000.

结果可从以下位置获得: https://www.google.com/fusiontables/DataSource?docid=1xkJhd4s8biLghe6gZccfUs3vT5MpS_OnscjWDbM
我创建了另一个版本,其中删除了每个重复的数据点。换句话说,仅显示与先前不同的点。这样可以更容易地看到异常。

The results are available at: https://www.google.com/fusiontables/DataSource?docid=1xkJhd4s8biLghe6gZbcfUs3vT5MpS_OnscjWDbM I've created another version where every repeated data point is removed. In other words, only points that are different from the previous are shown. This makes it easier to see anomalies. https://www.google.com/fusiontables/DataSource?docid=1XG_SRzrrNasepwZoNHqEAKuZlHiAm9vbEdwfsUA

这篇关于为什么这种方法打印4?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆