什么是StackOverflowError?

29 浏览
0 Comments

什么是StackOverflowError?

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

admin 更改状态以发布 2023年5月23日
0
0 Comments

首先让我们了解一下 局部 变量和对象是如何储存的。

局部变量储存在 中:

Enter image description here

如果你看了这张图片,你应该能够理解事情是如何工作的。

当 Java 应用程序调用一个函数调用时,在调用栈上分配了一个栈框架。栈框架包含被调用方法的参数、局部参数和方法的返回地址。返回地址指定程序执行从那里继续执行,这是在调用的方法返回后。如果没有新的栈框架空间,Java 虚拟机 (JVM) 将抛出 StackOverflowError

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

下面是一个抛出 StackOverflowError 的示例:

StackOverflowErrorExample.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);
    }
}

在这个例子中,我们定义了一个递归方法,称为 recursivePrint ,它打印一个整数,然后以下一个连续整数作为参数调用自己。递归以参数 0 结束。但是,在我们的例子中,我们传入了从 1 开始的参数及其递增的跟随者,因此递归永远不会终止。

下面是使用 -Xss1M 标志进行的示例执行,该标志指定线程堆栈的大小为 1 MB:

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[g|G|m|M|k|K]

0
0 Comments

参数和局部变量在栈上分配(对于引用类型,对象在堆上,栈中的变量引用堆上的对象)。栈通常位于地址空间的上端,随着使用它而向地址空间的底部移动(即朝向零)。\n\n进程还有一个堆,位于进程的底部。当你分配内存时,该堆可以向地址空间的上端扩展。正如你所看到的,堆和堆可能会“碰撞”(有点像构造板块!)。\n\n堆栈溢出的常见原因是错误的递归调用。通常,当你的递归函数没有正确的终止条件时,它会不停地调用自身。或者当终止条件正确时,可能会出现需要太多递归调用才能实现该条件。\n\n然而,在 GUI 编程中,可能会生成间接递归。例如,你的应用程序可能正在处理绘图消息,处理它们时,可能调用导致系统发送另一个绘图消息的函数。这里你没有显式地调用自己,但操作系统/虚拟机已经为你做了。\n\n为了处理它们,你需要检查你的代码。如果有调用自己的函数,请检查是否有终止条件。如果有,请检查在调用函数时是否至少修改了一个参数,否则递归调用函数将没有任何可见的更改,终止条件也就无用了。同时要注意,你的栈空间在达到有效的终止条件之前可能会耗尽内存,因此确保你的方法可以处理需要更多递归调用的输入值。\n\n如果没有明显的递归函数,请检查是否调用了任何会间接导致你的函数被调用的库函数(如上面的隐式情况)。

0