【问题标题】:Why doesn't a stack overflow always occur?为什么不总是发生堆栈溢出?
【发布时间】:2014-09-19 16:14:49
【问题描述】:

我目前正在学习 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);
    }
}

我的问题如下:

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

【问题讨论】:

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

标签: java memory stack-overflow


【解决方案1】:

有限的堆栈大小取决于您分配给 JVM 的内存量。

资源受限的系统可分配的内存较少,因此绝对存在堆栈大小受到限制的实际场景,并且有时您必须自然地使用迭代解决方案- 递归问题。

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

【讨论】:

    【解决方案2】:

    发生 StackOverflowException 的机会取决于您分配了多少内存,使用参数 XmxM/G 为最大内存,XmxS/G 为最少。

    在任何情况下都可能发生堆栈溢出,如果存在死循环,或者有可能通过自循环大量数据(这会耗尽大量内存)来实现这一点。

    【讨论】:

      【解决方案3】:

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

      如果您想要引发溢出,请尝试在递归调用之后添加一些代码以防止优化。您可能需要尝试一下——好的编译器很狡猾,可能会颠覆简单的尝试。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-09
        • 2012-05-10
        • 2018-07-01
        • 2010-09-11
        • 1970-01-01
        • 1970-01-01
        • 2017-08-17
        • 2012-12-19
        相关资源
        最近更新 更多