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


当前回答

在紧急情况下,以下情况将导致堆栈溢出错误。

public class Example3 {

    public static void main(String[] args) {

        main(new String[1]);
    }

}

其他回答

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

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

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

但是,使用GUI编程,可以生成间接递归。例如,你的应用程序可能正在处理paint消息,在处理它们的同时,它可能会调用一个函数,导致系统发送另一个paint消息。这里您没有显式地调用自己,但是OS/VM已经为您完成了。

To deal with them, you'll need to examine your code. If you've got functions that call themselves then check that you've got a terminating condition. If you have, then check that when calling the function you have at least modified one of the arguments, otherwise there'll be no visible change for the recursively called function and the terminating condition is useless. Also mind that your stack space can run out of memory before reaching a valid terminating condition, thus make sure your method can handle input values requiring more recursive calls.

如果没有明显的递归函数,则检查是否调用了间接导致函数被调用的库函数(如上面的隐式情况)。

如果你有这样一个函数:

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

然后foo()将继续调用自己,越调用越深,当用于跟踪所处函数的空间被填满时,就会得到堆栈溢出错误。

下面是一个递归算法的例子,用于反转单链表。在笔记本电脑(规格为4gb内存,Intel Core i5 2.3 GHz CPU 64位,Windows 7)上,对于大小接近10,000的链表,该函数将遇到StackOverflow错误。

我的观点是,我们应该明智地使用递归,始终考虑到系统的规模。

通常递归可以转换为迭代程序,迭代程序的伸缩性更好。(本页底部给出了同一算法的一个迭代版本。它在9毫秒内反转大小为100万的单链表。)

private static LinkedListNode doReverseRecursively(LinkedListNode x, LinkedListNode first){

    LinkedListNode second = first.next;

    first.next = x;

    if(second != null){
        return doReverseRecursively(first, second);
    }else{
        return first;
    }
}


public static LinkedListNode reverseRecursively(LinkedListNode head){
    return doReverseRecursively(null, head);
}

同一算法的迭代版本:

public static LinkedListNode reverseIteratively(LinkedListNode head){
    return doReverseIteratively(null, head);
}


private static LinkedListNode doReverseIteratively(LinkedListNode x, LinkedListNode first) {

    while (first != null) {
        LinkedListNode second = first.next;
        first.next = x;
        x = first;

        if (second == null) {
            break;
        } else {
            first = second;
        }
    }
    return first;
}


public static LinkedListNode reverseIteratively(LinkedListNode head){
    return doReverseIteratively(null, head);
}

堆栈溢出最常见的原因是过于深入或无限递归。如果这是你的问题,这个关于Java递归的教程可以帮助你理解这个问题。

堆栈溢出通常是由于嵌套函数调用太深(在使用递归时尤其容易,即函数调用自身)或在堆栈上分配大量内存而使用堆更合适。