什么是StackOverflowError?

什么是,是什么原因造成的,我应该如何处理它们?StackOverflowError


答案 1

参数和局部变量在堆栈上分配(对于引用类型,对象位于上,堆栈中的变量引用堆上的该对象)。堆栈通常位于地址空间的端,当它被用完时,它会朝向地址空间的底部(即朝向零)。

您的流程还有一个,该堆位于流程的端。分配内存时,此堆可能会向地址空间的上端增长。如您所见,堆有可能与堆栈“碰撞”(有点像构造板块!!!)。

堆栈溢出的常见原因是错误的递归调用。通常,这是在递归函数没有正确的终止条件时引起的,因此它最终会永远调用自身。或者,当终止条件正常时,可能是由于在满足之前需要过多的递归调用而导致的。

但是,通过 GUI 编程,可以生成间接递归。例如,你的应用可能正在处理绘制消息,并且在处理这些消息时,它可能会调用导致系统发送另一条绘制消息的函数。在这里,您没有明确称呼自己,但 OS/VM 已为您完成此操作。

要处理它们,您需要检查代码。如果你有调用自己的函数,那么检查你是否有终止条件。如果有,请检查在调用函数时是否至少修改了其中一个参数,否则递归调用的函数将没有可见的变化,并且终止条件是无用的。还要注意,在达到有效的终止条件之前,堆栈空间可能会耗尽内存,因此请确保您的方法可以处理需要更多递归调用的输入值。

如果你没有明显的递归函数,那么检查你是否正在调用任何间接导致你的函数被调用的库函数(如上面的隐式情况)。


答案 2

为了描述这一点,首先让我们了解局部变量和对象是如何存储的。

局部变量存储在堆栈上:

Enter image description here

如果你看了一下图像,你应该能够理解事情是如何运作的。

当 Java 应用程序调用函数调用时,将在调用堆栈上分配一个堆栈帧。堆栈帧包含被调用方法的参数、其本地参数以及该方法的返回地址。返回地址表示在调用的方法返回后程序执行应从该执行点继续执行。如果没有空间容纳新的堆栈帧,则 Java 虚拟机 (JVM) 将抛出 。StackOverflowError

可能耗尽 Java 应用程序堆栈的最常见情况是递归。在递归中,方法在执行期间调用自身。递归被认为是一种功能强大的通用编程技术,但必须谨慎使用,以避免 。StackOverflowError

抛出的示例如下所示:StackOverflowError

StackOverflowError示例.java:

public class StackOverflowErrorExample {

    public static void recursivePrint(int num) {
        System.out.println("Number: " + num);
        if (num == 0)
            return;
        else
            recursivePrint(++num);
        }

    public static void main(String[] args) {
        StackOverflowErrorExample.recursivePrint(1);
    }
}

在此示例中,我们定义了一个递归方法,称为该方法,该方法打印一个整数,然后调用自身,并将下一个连续整数作为参数。递归结束,直到我们作为参数传入。但是,在我们的示例中,我们从 1 传入参数及其递增的关注者,因此,递归永远不会终止。recursivePrint0

示例执行,使用将线程堆栈的大小指定为等于 1 MB 的标志,如下所示:-Xss1M

Number: 1
Number: 2
Number: 3
...
Number: 6262
Number: 6263
Number: 6264
Number: 6265
Number: 6266
Exception in thread "main" java.lang.StackOverflowError
        at java.io.PrintStream.write(PrintStream.java:480)
        at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
        at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
        at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
        at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
        at java.io.PrintStream.write(PrintStream.java:527)
        at java.io.PrintStream.print(PrintStream.java:669)
        at java.io.PrintStream.println(PrintStream.java:806)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        ...

根据 JVM 的初始配置,结果可能会有所不同,但最终应抛出。此示例是一个很好的例子,说明如果不谨慎实现递归,则递归如何导致问题。StackOverflowError

如何处理StackOverflowError

  1. 最简单的解决方案是仔细检查堆栈迹线并检测行号的重复模式。这些行号指示以递归方式调用的代码。检测到这些行后,必须仔细检查代码并了解递归永远不会终止的原因。

  2. 如果您已验证递归已正确实现,则可以增加堆栈的大小,以允许更多数量的调用。根据安装的 Java 虚拟机 (JVM),缺省线程堆栈大小可能等于 512 KB 或 1 MB。您可以使用标志增加线程堆栈大小。可以通过项目的配置或命令行指定此标志。参数的格式为:-Xss-Xss-Xss<size>[g|G|m|M|k|K]


推荐