当前位置: 首页 > 知识库问答 >
问题:

如何预测递归方法的最大调用深度?

陶弘业
2023-03-14

为了估计递归方法在给定内存量下可能达到的最大调用深度,在可能发生堆栈溢出错误之前,计算所用内存的(近似)公式是什么?

许多人回答说“视情况而定”,这是合理的,所以让我们用一个琐碎但具体的例子来删除一些变量:

public static int sumOneToN(int n) {
    return n < 2 ? 1 : n + sumOneToN(n - 1);
}

很容易看出,在我的EclipseIDE中运行它时,n的爆炸性增长不到1000(对我来说,这个数字低得出奇)。这个调用深度限制是否可以在不执行的情况下进行估计?

编辑:我忍不住认为Eclipse有一个固定的最大调用深度1000,因为我得到了998,但有一个用于main,还有一个用于方法的初始调用,使1000。这是一个“太圆”的数字,不可能是巧合。我会进一步调查的。我只是在-Xss vm参数上增加了Dux开销;这是最大堆栈大小,因此Eclipse runner必须在某个地方设置-Xss1000

共有3个答案

郁博学
2023-03-14

补充NPEs答案:

最大堆叠深度似乎是灵活的。下面的测试程序打印出了截然不同的数字:

public class StackDepthTest {
    static int i = 0;

    public static void main(String[] args) throws Throwable {
        for(int i=0; i<10; ++i){
            testInstance();
        }
    }

    public static void testInstance() {
        StackDepthTest sdt = new StackDepthTest();
        try {
            i=0;
            sdt.instanceCall();
        } catch(StackOverflowError e){}
        System.out.println(i);
    }

    public void instanceCall(){
        ++i;
        instanceCall();
    }
}

输出为:

10825
10825
59538
59538
59538
59538
59538
59538
59538
59538

我使用了这个JRE的默认设置:

java version "1.7.0_09"
OpenJDK Runtime Environment (IcedTea7 2.3.3) (7u9-2.3.3-0ubuntu1~12.04.1)
OpenJDK 64-Bit Server VM (build 23.2-b09, mixed mode)

所以结论是:如果你有足够的(即两次以上),你会得到第二次机会;-)

况胡媚
2023-03-14

只有部分答案:根据JVM规范7,2.5.2,可以在堆上分配堆栈帧,并且堆栈大小可能是动态的。我不能确定,但似乎应该可以让堆栈大小仅受堆大小的限制:

因为Java虚拟机堆栈除了推送和弹出帧外,从不直接操作,所以帧可以堆分配。

该规范允许Java虚拟机堆栈具有固定大小,或者根据计算需要动态扩展和收缩。如果Java虚拟机堆栈的大小是固定的,那么在创建该堆栈时,可以独立选择每个Java虚拟机堆栈的大小。

Java虚拟机实现可以为程序员或用户提供对Java虚拟机栈的初始大小的控制,以及在动态扩展或收缩Java虚拟机栈的情况下,对最大和最小大小的控制。

所以这取决于JVM实现。

云伯寅
2023-03-14

这显然是JVM,也可能是特定于架构的。

我测量了以下各项:

  static int i = 0;
  public static void rec0() {
      i++;
      rec0();
  }

  public static void main(String[] args) {
      ...
      try {
          i = 0; rec0();
      } catch (StackOverflowError e) {
          System.out.println(i);
      }
      ...
  }

使用

Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)

在x86上运行。

对于20MB的Java堆栈(-Xss20m),每个调用的摊销成本在16-17字节左右波动。我见过的最低值是16.15字节/帧。因此,我得出结论,成本是16字节,其余是其他(固定)开销。

使用单个int的函数具有基本相同的成本,16字节/帧。

有趣的是,一个需要10个整数的函数需要32字节/帧。我不知道为什么成本这么低。

上述结果适用于JIT编译后的代码。在编译之前,每帧成本要高得多。我还没有找到可靠的方法来估计它。然而,这确实意味着在您能够可靠地预测递归函数是否已经被JIT编译之前,您不可能可靠地预测最大递归深度。

所有这些都是用128K和8MB的ulimit堆栈进行测试的。两种情况下的结果相同。

 类似资料:
  • 问题内容: 为了估计在给定内存量下递归方法可能实现的最大调用深度,在可能发生堆栈溢出错误之前,用于计算所用内存的(近似)公式是什么? 编辑: 许多人以“取决于”来回答,这是合理的,因此让我们通过一个简单但具体的示例来删除一些变量: 很容易看出,在我的Eclipse IDE中运行该命令时爆炸的次数不到1000(对我来说这很低)。是否可以在不执行此调用深度限制的情况下进行估算? 编辑:我不禁想到Ecl

  • 问题内容: 我从星期一开始使用Python进行编程。我很喜欢学习它。但是我一直试图了解如何在tkinter菜单之间切换时避免递归!我确信这是一个非常基本的问题,感谢您宽容我对此主题的无知,但我无法在其他地方找到答案。 我现在正在做的最终是给我错误:RuntimeError:调用Python对象时超出了最大递归深度 这是我目前正在使用的模式。更新:下面的代码现在是完整的隔离副本,再现了我面临的问题!

  • 我对Python很陌生。我写了一个关于返回 x 在排序的重复元素数组 A 中的出现次数的函数: 错误是:运行时错误:超出最大递归深度。有人知道如何解决它吗?

  • 问题内容: 背景:我正在使用最小构造算法构建一个Trie以表示字典。输入列表是430万个utf-8字符串,按字典顺序排序。生成的图是非循环的,最大深度为638个节点。我的脚本的第一行通过将递归限制设置为1100 。 问题:我希望能够将Trie序列化到磁盘上,因此我可以将其加载到内存中,而不必从头开始重建(大约22分钟)。我已经尝试了和,同时使用了文本和二进制协议。每次,我都会得到如下所示的堆栈跟踪

  • 我不明白为什么我会得到这个最大深度错误。iam试图使用bst递归方法在数组中查找数字索引,下面是我的代码 任何人都可以告诉我代码块中发生了什么 错误块: PS C:\Users\admin\Desktop\DSA

  • 问题内容: 这个尾部递归函数: 它工作到了,然后它破裂并吐出了。这只是堆栈溢出吗?有办法解决吗? 问题答案: 是的,可以防止堆栈溢出。Python(或更确切地说,CPython实现)不能优化尾部递归,无限制的递归会导致堆栈溢出。你可以使用来检查递归限制,并使用来更改递归限制,但是这样做很危险-标准限制有些保守,但是Python堆栈框架可能会很大。 Python不是一种功能语言,尾部递归并不是一种特