首页 > 解决方案 > 为什么递归函数会在随机数上停止?

问题描述

我编写了一个如下所示的小程序,它计算无限递归循环在导致 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 或命令行对其进行测试。

标签: javarecursion

解决方案


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 Virtual机器抛出 OutOfMemoryError。

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

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

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


推荐阅读