什么是StackOverflowError?

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


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

你的过程也有一堆,它存在于你的过程的底端。 当你分配内存时,这个堆可以增长到你的地址空间的高端。 正如你所看到的,堆有可能与栈“碰撞”(有点像构造板!!!)。

堆栈溢出的常见原因是递归调用不好。 通常,这是由于递归函数没有正确的终止条件导致的,所以它最终会永远调用它自己。 但是,使用GUI编程,可以生成间接递归。 例如,您的应用程序可能正在处理绘画消息,并且在处理它们时,它可能会调用导致系统发送另一个绘画消息的函数。 在这里你没有明确地称呼你自己,但是OS / VM已经为你做了。

要处理它们,你需要检查你的代码。 如果你有自称的函数然后检查你是否有终止条件。 如果你已经检查了,而不是在调用函数时至少修改了一个参数,否则对于递归调用的函数将没有可见的变化,并且终止条件是无用的。

如果你没有明确的递归函数,那么检查一下你是否调用了间接会导致函数被调用的任何库函数(就像上面的隐式情况)。


为了描述这一点,首先让我们了解局部变量和对象如何存储,局部变量是否存储在堆栈中 在这里输入图像描述

如果你看图像,你应该能够理解事物的运作方式。

当一个Java应用程序调用一个函数调用时,一个堆栈帧被分配到调用栈中。 堆栈帧包含被调用方法的参数,其本地参数和方法的返回地址。 返回地址表示执行点,在调用方法返回之后,程序执行应继续执行。 如果没有空间用于新的堆栈帧,那么StackOverflowError由Java虚拟机(JVM)抛出。 可能会耗尽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开始打印数字,因此递归永远不会终止。 下面显示了使用指定线程堆栈大小等于1MB的-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

  • 最简单的解决方案是仔细检查堆栈轨迹并检测行号的重复模式。 这些行号表示递归调用的代码。 一旦你检测到这些行,你必须仔细检查你的代码,并理解为什么递归永远不会终止。

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


  • 如果你有这样的功能:

    int foo()
    {
        // more stuff
        foo();
    }
    

    然后foo()会继续调用它自己,变得越来越深,当用于跟踪你所在功能的空间被填满时,你会得到堆栈溢出错误。

    链接地址: http://www.djcxy.com/p/80451.html

    上一篇: What is a StackOverflowError?

    下一篇: Unknown Malloc stack overflow c