当前位置: 首页 > 面试题库 >

为什么此方法打印4?

敖毅
2023-03-14
问题内容

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

class RandomNumberGenerator {

    static int cnt = 0;

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

现在我的问题是:

为什么此方法打印“ 4”?

我以为是因为System.out.println()在调用堆栈上需要3个段,但是我不知道3的来源。当您查看的源代码(和字节码)时System.out.println(),通常导致的方法调用要多于3(因此,调用堆栈上的3个段是不够的)。如果是由于优化而应用了Hotspot
VM(方法内联),我想知道其他VM上的结果是否会有所不同。

编辑

由于输出似乎是高度特定于JVM的,因此我使用 Java(TM)SE Runtime Environment(内部版本1.6.0_41-b02),
Java HotSpot(TM)64位服务器VM(内部版本20.14-b01,混合模式)获得了结果4。

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

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


问题答案:

我认为其他人在解释为什么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



 类似资料:
  • 我想知道当您试图捕获StackOverflowError时会发生什么,并提出了以下方法: 现在我的问题是: 为什么这个方法打印'4'? 我想可能是因为在调用堆栈上需要3个段,但我不知道3这个数字来自哪里。当您查看的源代码(和字节码)时,通常会导致比3个多得多的方法调用(因此调用堆栈中的3个段是不够的)。如果这是因为Hotspot VM应用的优化(方法内联),我想知道在另一个VM上结果是否会有所不同

  • 问题内容: 众所周知,(大多数)浮点数没有精确存储(使用IEEE-754格式时)。因此,不应这样做: ......因为它会导致,除非一些特定的任意精度的类型/类使用(BigDecimal的中的Java/Ruby的,bcmath时在PHP中,[数学::BigInt有 / 数学::BigFloat在Perl,仅举几例)来代替。 但是,我想知道为什么当尝试打印该表达式的结果时,脚本语言(Perl和PHP

  • 问题内容: 我生成了x的两个矩阵: 第一矩阵:和。 第二矩阵:和。 使用以下代码,第一个矩阵花费了8.52秒完成: 使用此代码,第二个矩阵花费了259.152秒来完成: 运行时间显着不同的原因是什么? 正如评论所说,仅打印需要秒,而给。 正如其他指出它对他们正常工作的人一样,例如,我尝试了Ideone.com,这两段代码以相同的速度执行。 测试条件: 我从 Netbeans 7.2 运行了此测试,

  • 所以我有这个代码,叫我白痴哈哈,但是我不能让它打印7。 代码: 我需要知道如何改变与函数中的参数同名的变量。 任何帮助都将不胜感激,如果你不明白这个问题,我很乐意解释更多。

  • 在python为什么 我想当时,这应该是正确的。应该给我,但是为什么没有?如果这是对的,为什么当,它给我?

  • 问题内容: 我不明白为什么编译器从字典中打印值会有所不同?我使用的是Swift 3,Xcode8。这是错误还是优化内容的怪异方法? 编辑 更奇怪的是: 有些价值观过去了,有些则跌破了,有些则保持不变!1.1小于1.1000000000000001,而2.3大于2.2999999999999998,1.2仅为1.2 问题答案: 如注释中已经提到的,a 不能精确存储值。Swift根据IEEE 754