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>