java - 为什么递归函数会在随机数上停止?
问题描述
我编写了一个如下所示的小程序,它计算无限递归循环在导致 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 或命令行对其进行测试。
解决方案
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 错误之前看到几乎相同数量的递归。
推荐阅读
- python - 为什么在我的 Flask 应用程序中调用 csrf_token() 会引发“无法将元组连接到字节”错误?
- html - 在链接中使用 session.storage 变量
- snowflake-cloud-data-platform - 在 Snowflake 中通过 distinct 展平和聚合两列数组
- django - 根据分配的 user_group 保存后用户处于非活动状态 - Django Allauth
- python - 如何在 Windows 10 上的 VSCode 中激活 conda 环境
- vim - 为什么这个命令会“打嗝”?
- html - Outlook 电子邮件签名的简单 HTML 格式
- python - 依赖解析可视化
- python - 使用 Python 检查网站的状态变化
- python - 如何在我的网络应用程序上安全地接受和运行用户的代码?