为什么递归函数在随机数上停止?

2022-09-04 22:01:27

我写了一个小程序,如下所示,它计算无限递归循环在导致StackOverflow错误之前将经过多少次。

public class Testing {
    static void p(int i) {
        System.out.println("hello" + i);
        i++;
        p(i);
    }
    public static void main(String[] args) {
        p(1);
    }
}

问题是,它每次都会在不同的数字上出错,通常在8000和9000之间。谁能解释为什么会发生这种情况?

编辑:我正在使用Eclipse IDE,还没有用其他IDE或命令行测试过它。


答案 1

JVM规范很好地解释了它与堆栈相关的行为;

每个 Java 虚拟机线程都有一个私有 Java 虚拟机堆栈,与该线程同时创建。Java 虚拟机堆栈存储帧 (§2.6)。Java虚拟机堆栈类似于传统语言(如C)的堆栈:它保存局部变量和部分结果,并在方法调用和返回中发挥作用。由于 Java 虚拟机堆栈除了推送和弹出帧外,从不直接操作,因此可以堆分配帧。Java 虚拟机堆栈的内存不需要是连续的。

在 Java® 虚拟机规范的第一版中,Java 虚拟机堆栈被称为 Java 堆栈。

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

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

以下异常情况与 Java 虚拟机堆栈相关联:

如果线程中的计算需要的 Java 虚拟机堆栈大于允许的堆栈,则 Java 虚拟机会引发 StackOverflowError。

如果可以动态扩展 Java 虚拟机堆栈,并且尝试扩展,但没有足够的内存可用于实现扩展,或者如果没有足够的内存可用于为新线程创建初始 Java 虚拟机堆栈,则 Java 虚拟机将抛出一个 OutOfMemoryError。

就您的问题而言,本摘录中的一个重要观点是:

  • 此规范允许 Java 虚拟机堆栈具有固定大小,或者根据计算需要动态扩展和收缩。

由于您没有提供堆栈大小,因此 JVM 会尝试动态扩展堆栈大小,因为该函数被递归调用,需要更多的堆栈内存。在每次运行中,它可能会为其堆栈找到不同数量的动态内存,具体取决于该运行点计算机上内存的可用性。这就是您在引发 SO 错误之前看到的迭代次数的不同值的原因。如果为程序配置(使用 JVM 参数)较小的堆栈大小,则在 SO 错误之前,您应该会看到大致相同的递归数。Xss<size>


答案 2

可能与计算机可以分配给程序的实际内存量有关,而其他程序和进程正在计算机中运行


推荐