java - 多个递归方法调用的各种堆栈大小

标签 java eclipse recursion jvm stack-overflow

在实验过程中,我发现了与堆栈溢出错误相关的非常有趣的问题。 看一下这段代码:

Foo 类:

public class Foo {

    private int i;

    public void doFoo() {
        i++;
        doBar();
    }

    public void doBar() {
        doFoo();
    }

    public int getI() {
        return i;
    }

    public void reset() {
         i = 0;
    }
}

应用程序类别:

public class App {

    public static void main(String[] args) {
        Foo foo = new Foo();
        try {
            foo.doFoo();
        } catch (StackOverflowError e) {
            System.out.println(foo.getI());
        }
    }
}

很明显,递归调用doFoo和doBar方法会导致stackOverflowError。但是此后 foo.i 会有什么值呢?我已经使用默认虚拟机堆栈大小对其进行了几次测试 - 这就是 System.out.println(foo.getI());打印:

  • 第一次运行:4372
  • 第二次运行:8364
  • 第三次运行:3381
  • 第四次运行:8406
  • 第五次运行:3485

你能看到吗?持续运行此应用程序,i 变量将始终比上次运行大/小约 4500。

如果我们增加堆栈大小怎么办?添加时-Xss1m参数到VM args,结果如下:

  • 第一次运行:33364
  • 第二次运行:38404
  • 第三次运行:33787
  • 第四次运行:38434
  • 第五次运行:33805

都是一样的! i 值仍然比上次运行大/小约 4500!

然而,当我们将 main 方法更改为:

for (int i = 0; i < 10; i++) {
        try {
            foo.doFoo();
            foo.reset();
        } catch (StackOverflowError e) {
            System.out.println(foo.getI());
        }
    }

我们将得到可预测的结果:

  • 38398
  • 80796
  • 123194
  • 165592
  • 207990
  • 250388
  • 292786
  • 335184
  • 377582
  • 419980

可能是由于JVM初始化后堆栈空闲空间越来越多。但是,为什么一次又一次地调用程序时会出现约 4500 次振荡呢? 附言。我直接从 Eclipse 运行这个应用程序(如果重要的话)。

====

编辑:

好的,我现在可以看到,foo.reset()永远不会被调用,因为 Error 在它之前被抛出。当foo.dooFoo()foo.reset() 交换,我们现在有了恒定的结果:

  • 38394
  • 42394
  • 42394
  • 42394
  • 42394
  • 42394
  • 42394
  • 42394
  • 42394
  • 42394

但是,问题是为什么在运行程序时有大约 4500 次振荡,仍然是开放的。

==编辑2

此问题仅与直接从 Eclipse 运行(不带 -Xint 参数)有关。当程序从命令行启动时,java -cp . App ,i 更恒定:+/- 5。

最佳答案

我怀疑正在发生的事情(以及你的证据表明)是这些方法被频繁调用,以至于你的代码最终通过 JIT 编译器运行。当这种情况发生时,我推测优化可能会启动并认识到每次执行 doFoo 时递增 i 是不必要的——有点像展开循环.

我不完全确定为什么您会看到任何变化,除了 JIT 启动时可能存在不确定的成本组件,或者偶尔会启动几帧。

关于java - 多个递归方法调用的各种堆栈大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20937907/

相关文章:

java - 在 Eclipse 中调试递归方法——有什么聪明的方法可以避免陷入更多的嵌套调用?

recursion - 不在尾部位置重复

java - 有什么方法可以在 Java 中创建字符串文字类型?

java - 不会更新值

eclipse - 将 jar 添加到 Tomcat 类路径

c - 递归二分查找无法正常工作

c - 在C中递归删除单链表

java - 使用 JPA 2.1 注释调用 SQL Server 存储过程

java - 内部类 - 最终变量不能循环?

java - 创建可滚动区域有哪些技术?