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


当前回答

这是一个典型的java.lang.StackOverflowError…该方法递归地调用自身,在doubleValue()、floatValue()等中没有出口。

文件Rational.java

public class Rational extends Number implements Comparable<Rational> {
    private int num;
    private int denom;

    public Rational(int num, int denom) {
        this.num = num;
        this.denom = denom;
    }

    public int compareTo(Rational r) {
        if ((num / denom) - (r.num / r.denom) > 0) {
            return +1;
        } else if ((num / denom) - (r.num / r.denom) < 0) {
            return -1;
        }
        return 0;
    }

    public Rational add(Rational r) {
        return new Rational(num + r.num, denom + r.denom);
    }

    public Rational sub(Rational r) {
        return new Rational(num - r.num, denom - r.denom);
    }

    public Rational mul(Rational r) {
        return new Rational(num * r.num, denom * r.denom);
    }

    public Rational div(Rational r) {
        return new Rational(num * r.denom, denom * r.num);
    }

    public int gcd(Rational r) {
        int i = 1;
        while (i != 0) {
            i = denom % r.denom;
            denom = r.denom;
            r.denom = i;
        }
        return denom;
    }

    public String toString() {
        String a = num + "/" + denom;
        return a;
    }

    public double doubleValue() {
        return (double) doubleValue();
    }

    public float floatValue() {
        return (float) floatValue();
    }

    public int intValue() {
        return (int) intValue();
    }

    public long longValue() {
        return (long) longValue();
    }
}

文件Main.java

public class Main {

    public static void main(String[] args) {

        Rational a = new Rational(2, 4);
        Rational b = new Rational(2, 6);

        System.out.println(a + " + " + b + " = " + a.add(b));
        System.out.println(a + " - " + b + " = " + a.sub(b));
        System.out.println(a + " * " + b + " = " + a.mul(b));
        System.out.println(a + " / " + b + " = " + a.div(b));

        Rational[] arr = {new Rational(7, 1), new Rational(6, 1),
                new Rational(5, 1), new Rational(4, 1),
                new Rational(3, 1), new Rational(2, 1),
                new Rational(1, 1), new Rational(1, 2),
                new Rational(1, 3), new Rational(1, 4),
                new Rational(1, 5), new Rational(1, 6),
                new Rational(1, 7), new Rational(1, 8),
                new Rational(1, 9), new Rational(0, 1)};

        selectSort(arr);

        for (int i = 0; i < arr.length - 1; ++i) {
            if (arr[i].compareTo(arr[i + 1]) > 0) {
                System.exit(1);
            }
        }


        Number n = new Rational(3, 2);

        System.out.println(n.doubleValue());
        System.out.println(n.floatValue());
        System.out.println(n.intValue());
        System.out.println(n.longValue());
    }

    public static <T extends Comparable<? super T>> void selectSort(T[] array) {

        T temp;
        int mini;

        for (int i = 0; i < array.length - 1; ++i) {

            mini = i;

            for (int j = i + 1; j < array.length; ++j) {
                if (array[j].compareTo(array[mini]) < 0) {
                    mini = j;
                }
            }

            if (i != mini) {
                temp = array[i];
                array[i] = array[mini];
                array[mini] = temp;
            }
        }
    }
}

结果

2/4 + 2/6 = 4/10
Exception in thread "main" java.lang.StackOverflowError
2/4 - 2/6 = 0/-2
    at com.xetrasu.Rational.doubleValue(Rational.java:64)
2/4 * 2/6 = 4/24
    at com.xetrasu.Rational.doubleValue(Rational.java:64)
2/4 / 2/6 = 12/8
    at com.xetrasu.Rational.doubleValue(Rational.java:64)
    at com.xetrasu.Rational.doubleValue(Rational.java:64)
    at com.xetrasu.Rational.doubleValue(Rational.java:64)
    at com.xetrasu.Rational.doubleValue(Rational.java:64)
    at com.xetrasu.Rational.doubleValue(Rational.java:64)

下面是OpenJDK 7中StackOverflowError的源代码。

其他回答

堆栈溢出的意思就是:堆栈溢出。通常在程序中有一个堆栈,它包含局部作用域变量和例程执行结束时返回的地址。该堆栈往往是内存中的某个固定内存范围,因此它可以包含多少值是有限的。

如果堆栈是空的,你不能弹出,如果你这样做,你会得到堆栈溢出错误。

如果堆栈满了,你就不能推,如果你推了,你就会得到堆栈溢出错误。

当你在堆栈中分配了太多内存时,就会出现堆栈溢出。例如,在前面提到的递归中。

有些实现优化了某些形式的递归。特别是尾递归。尾递归例程是一种例程,其中递归调用作为例程所做的最后一件事出现。这样的例行调用被简化为跳转。

有些实现甚至实现了自己的递归堆栈,因此它们允许递归继续进行,直到系统内存耗尽。

你可以尝试的最简单的事情就是如果可以的话增加你的堆栈大小。如果不能这样做,那么第二种方法是查看是否有什么东西明显导致堆栈溢出。试着在调用前后打印一些东西到例程中。这可以帮助您找到失败的例程。

如果你有这样一个函数:

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

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

堆栈有一个空间限制,这取决于操作系统。正常的大小是8mb(在Ubuntu (Linux)中,你可以用$ ulimit -u检查这个限制,在其他操作系统中也可以类似地检查)。任何程序都在运行时使用堆栈,但要完全了解何时使用堆栈,您需要检查汇编语言。例如,在x86_64中,堆栈用于:

在进行过程调用时保存返回地址 保存本地变量 保存特殊寄存器以便稍后恢复它们 向过程调用传递参数(大于6) 其他:随机未使用的堆栈基础,金丝雀值,填充,…等。

如果您不知道x86_64(一般情况下),您只需要知道您所使用的特定高级编程语言何时编译这些操作。例如在C语言中:

→函数调用 (2)→函数调用中的局部变量(包括main) (3)→函数调用中的局部变量(不是main) →函数调用 (5)→通常是一个函数调用,通常与堆栈溢出无关。

因此,在C语言中,只有局部变量和函数调用使用堆栈。造成堆栈溢出的两种(唯一的?)方法是:

在main或任何函数中声明过大的局部变量(int array[10000][10000];) 深度递归或无限递归(同时调用太多函数)。

要避免StackOverflowError,您可以:

检查局部变量是否太大(1mb数量级)→使用堆(malloc/calloc调用)或全局变量。 检查无限递归→你知道该怎么做…正确的! 检查常规的深度递归→最简单的方法是将实现更改为迭代。

还要注意全局变量、包含库等等……不要使用堆栈。

只有当上述方法不起作用时,才可以在特定的操作系统上将堆栈大小更改为最大值。例如Ubuntu: ulimit -s 32768 (32 MB)。(这从来都不是我的任何堆栈溢出错误的解决方案,但我也没有太多经验。)

我省略了C语言中的特殊和/或非标准情况(例如alloc()和类似的用法),因为如果你正在使用它们,你应该已经确切地知道你在做什么。

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

一个简单的Java示例,由于错误的递归调用导致Java .lang. stackoverflowerror:

class Human {
    Human(){
        new Animal();
    }
}

class Animal extends Human {
    Animal(){
        super();
    }
}

public class Test01 {
    public static void main(String[] args) {
        new Animal();
    }
}