Java 为什么';堆栈溢出是否总是发生?

Java 为什么';堆栈溢出是否总是发生?,java,memory,stack-overflow,Java,Memory,Stack Overflow,我目前正在学习Java,作为我学习的一部分,我试图故意诱导堆栈溢出,看看它会做什么 我做了一些边界测试,有趣的是,我发现如果我执行下面的代码,它只会偶尔导致错误。有时它运行起来没有任何问题 public class SO { public static void main(String[] args) { ohno(0); } public static void ohno(int a) { System.out.prin

我目前正在学习Java,作为我学习的一部分,我试图故意诱导堆栈溢出,看看它会做什么

我做了一些边界测试,有趣的是,我发现如果我执行下面的代码,它只会偶尔导致错误。有时它运行起来没有任何问题

public class SO
{
    public static void main(String[] args)
    {
        ohno(0);
    }

    public static void ohno(int a)
    {
        System.out.println(a);
        if (a != 11413)
            ohno(a+1);
    }
}
我的问题如下:

  • 是什么原因导致我的堆栈大小在这个非常简单的示例的执行之间发生变化
  • 现在,堆栈溢出是否总是由于糟糕的代码设计(即无限递归、过大的原语等)而发生,或者是否存在堆栈仍然是技术限制的真实场景
  • 这一点似乎很明显,但。。增加系统的物理内存是否也会增加堆栈的大小

    • 有限堆栈大小是分配给JVM的内存量的函数

      资源受限的系统可以分配的内存较少,因此在现实世界中,堆栈大小是一个限制,有时您必须使用迭代解决方案来解决自然递归问题


      只有当允许JVM分配系统的物理内存时,增加系统的物理内存才有意义,否则您将获得该平台的默认值。

      发生StackOverflowException的可能性取决于您分配了多少内存,使用参数XmxM/G表示最大内存,使用参数XmxS/G表示最小内存


      如果存在死循环,任何情况下都可能发生堆栈溢出,或者可能通过使用大量数据(消耗大量内存)进行自循环来实现堆栈溢出。

      许多编译器检测尾部递归调用,并为该调用重用同一堆栈帧,从而防止此类调用增加堆栈。尾部递归调用是递归调用是方法或函数中的最后一条指令。这种优化允许您使用尾部递归,而不必担心堆栈溢出


      如果希望导致溢出,请尝试在递归调用之后添加一些代码以防止优化。您可能需要稍微玩弄一下——好的编译器很狡猾,可能会破坏简单的尝试。

      至于最后一个问题:不。首先,关于Java方面,请记住Java是一个“虚拟机”,它独立于它运行的实际系统。这意味着它将处理自己的堆栈,并将其与普通操作系统/硬件堆栈分开。其次,底层操作系统通常有固定大小的进程堆栈,因此添加更多内存不会增加堆栈。