Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何预测递归方法的最大调用深度?_Java_Memory_Recursion_Jvm_Stack Overflow - Fatal编程技术网

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

Java 如何预测递归方法的最大调用深度?,java,memory,recursion,jvm,stack-overflow,Java,Memory,Recursion,Jvm,Stack Overflow,为了估计递归方法在给定内存量下可能达到的最大调用深度,在可能发生堆栈溢出错误之前,计算所用内存的(近似)公式是什么 编辑: 许多人的回答是“视情况而定”,这是合理的,因此让我们使用一个简单但具体的例子来删除一些变量: public static int sumOneToN(int n) { return n < 2 ? 1 : n + sumOneToN(n - 1); } 公共静态int-sumOneToN(int-n){ 返回n

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

编辑: 许多人的回答是“视情况而定”,这是合理的,因此让我们使用一个简单但具体的例子来删除一些变量:

public static int sumOneToN(int n) {
    return n < 2 ? 1 : n + sumOneToN(n - 1);
}
公共静态int-sumOneToN(int-n){
返回n<2?1:n+sumOneToN(n-1);
}
很容易看出,在我的EclipseIDE中运行它会使
n
爆炸性地接近1000(对我来说低得出奇)。 在不执行的情况下,是否可以估计此调用深度限制


编辑:我忍不住认为Eclipse有一个固定的最大调用深度1000,因为我得到了
998
,但是有一个用于main,还有一个用于方法的初始调用,总共使
1000
。这是一个“太圆”的数字,不可能是巧合。我会进一步调查的。我只需要Dux开销-Xss vm参数;这是最大堆栈大小,因此Eclipse runner必须在某个地方设置
-Xss1000
只有部分答案:从,堆栈帧可以在堆上分配,并且堆栈大小可能是动态的。我不能确定,但似乎应该可以让堆栈大小仅受堆大小的限制:

由于Java虚拟机堆栈除了用于推送和弹出帧之外从不直接操作,因此帧可能是堆分配的

该规范允许Java虚拟机堆栈 一个固定的大小或大小,可以根据 计算。如果Java虚拟机堆栈的大小固定, 可以选择每个Java虚拟机堆栈的大小 在创建堆栈时独立运行

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


因此,这将取决于JVM的实现。

答案是,这取决于具体情况

首先,可以更改Java堆栈的大小


其次,给定方法的堆栈帧大小可以根据不同的变量而变化。您可以查看的调用堆栈帧大小部分了解更多详细信息。

取决于您的系统体系结构(32或64位地址)、本地变量的数量和方法的参数。 如果没有开销,如果编译器将其优化为循环


你看,没有简单的答案。

这显然是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
堆栈大小进行测试的。两种情况下的结果都是一样的。

你可以走经验道路,用你的代码和
-Xss
设置进行实验。有关更多信息,请参见此处:

添加到NPE答案:

最大堆栈深度似乎是灵活的。以下测试程序打印了大量不同的数字:

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();
    }
}
我使用了此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)

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

(这似乎取决于JVM,可能还取决于架构。)我猜这只是局部变量+帧指针+返回地址。我还认为,如果不测量递归,就不可能进行估计。递归与此无关(除非它是生成真正深层调用堆栈的最常用方法)。为了确定调用深度限制,递归调用与任何其他类型的调用没有区别。(当然,编译器可能会消除尾部递归,在这种情况下,递归会让你的问题分心。)这被标记为“java”,因此我们假设我们在这里讨论的是JVM。@RyanStewart有32位和64位JVM,但JVM中的内存工作原理是一样的。JVM中也没有尾部递归,我也不知道有哪种java编译器会进行这样的优化。因此,在我看来,你一定在谈论别的东西。@Ryan Stewart:我刚刚在google上搜索了一下,很震惊地发现java还没有尾部递归优化。但可能是在将来。堆栈帧可能是堆分配的,因此帧大小可能无关,取决于。你是如何测量递归深度的?他/她可以使用静态成员字段轻松测量递归深度,该字段在每次调用时递增。在64位系统上,每个堆栈帧大约16字节看起来像一个返回地址+一个额外地址(帧指针?
这是一个
指针(从技术上讲,它是
null
,并非不存在)?)@Bohemian:我已经扩展了答案(昨晚没有时间这么做)。@JanDvorak:我想根本就不会有
这个
。我只能猜第二个8字节是用来做什么的。很可能是某种指针,真的吗
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)