7

在 Eclipse 中执行下面粘贴的代码时,大约有 3 次我遇到以下异常:

Exception in thread "main" java.lang.StackOverflowError
    at src.Adder.recursiveSumAllNumbersUpTo(Driver.java:33)
    at src.Adder.recursiveSumAllNumbersUpTo(Driver.java:37)
    ... *(there are 1024 lines in this stack)*

另外 2 次,它按预期吐出结果(每次运行之间的时间略有不同):

Recursive: 467946
Non Recursive: 61282
Difference between recursive and non-recursive: 406664
Sum from recursive add: 19534375
Sum from non-recursive add: 19534375

为什么异常只发生(似乎)〜30%的时间?

这是代码:

public class Driver {

    public static void main(String[] args) {

        Adder adder = new Adder();
        int valueToSumTo = 6250;

        long startTime = System.nanoTime();
        int raSum = adder.recursiveAddAllNumbersUpTo(valueToSumTo);
        long endTime = System.nanoTime();
        long raDif = endTime - startTime;
        System.out.println("Recursive: " + (raDif));

        startTime = System.nanoTime();
        int nonRaSum = adder.nonRecursiveAddAllNumbersUpTo(valueToSumTo);
        endTime = System.nanoTime();
        long nonRaDif = endTime - startTime;
        System.out.println("Non Recursive: " + (nonRaDif));

        System.out.println("Difference between recursive and non-recursive: " + (raDif - nonRaDif));
        System.out.println("Sum from recursive add: " + raSum);
        System.out.println("Sum from non-recursive add: " + nonRaSum);
    }
}

class Adder {

    public int recursiveAddAllNumbersUpTo(int i) {

        if (i == 1) {
            return i;
        }

        return i + recursiveAddAllNumbersUpTo(i - 1);
    }

    public int nonRecursiveAddAllNumbersUpTo(int i) {
        int count = 0;
        for(int num = 1; num <= i; num++) {
            count += num;
        }

        return count;
    }   
}
4

3 回答 3

2

每次递归方法调用自身时,所有内容都会进入堆栈,并且像您这样的算法需要非常深的堆栈。

要解决您的问题,您应该增加堆栈大小。

您可以通过使用 -Xss 参数调用 JVM 来做到这一点。

如果您使用的是 Eclipse,则可以通过执行以下操作来做到这一点:

  1. 右键单击您的项目-> 运行方式-> 运行配置;
  2. 转到参数选项卡;在 VM 参数处,写入:“-Xss4m”

如果该配置还不够,请尝试更大的堆栈。

于 2013-09-19T15:18:44.253 回答
0

玩堆栈大小:-Xss32m允许堆栈 32 Mo。
注意:AFAIK,java 不处理(为什么?!)尾递归优化。

于 2013-09-19T15:12:53.810 回答
0

尝试看看是否有用:

来自(http://www.odi.ch/weblog/posting.php?posting=411

Java 堆栈大小神话

Java 如何使用堆栈基本上没有记录。因此,我们开发人员只能自己找出答案。今天我写了一些有趣的测试代码,结果令人惊讶。对于我的测试,我在 RedHat Enterprise Linux 3 下使用了 Sun JDK 1.5.0_12。

首先,我想知道 VM 为线程选择的堆栈大小。我发现 ulimit -s 参数对 VM 选择的堆栈大小没有直接影响。默认值显然是 512kb,并且可以使用 -Xss 和 -XX:ThreadStackSize 参数自由更改。但我找不到这些参数之间的行为差​​异。他们似乎在做同样的事情。此外,我发现这台 Linux 机器能够以每秒约 5000 个的速度创建新线程。

我通过创建新线程并立即在阻塞等待调用中将它们设置为睡眠状态来执行这些测试。我一直在创建线程,直到 VM 内存不足。对于 512k 堆栈,线程数约为 3700,对于 256k 堆栈,约为 7300,对于 128k,约为 13700。

这导致堆栈消耗以下内存: 3700 x 512kB = 1850MB 7300 x 256kB = 1825MB 13700 x 128kB = 1713MB

当然,32 位进程被限制为 4GB 的地址空间(内核减去 1 或 2 GB)。所以内存接近这 2GB 减去堆大小是很自然的。(请注意,堆栈永远不会从堆中分配。)

接下来,我测试了调用递归方法的深度,直到堆栈溢出。我通过修改初始程序来做到这一点,这样每个线程都会递归到一个方法中,直到它遇到堆栈溢出,然后进入睡眠状态。线程记录了最大递归深度。那是我得到非常奇怪的结果的地方。特别是对于服务器 VM,最大深度在 1750 到 5700 次调用(128k 堆栈)之间的很大范围内变化!这远非恒定,这将是我的第一个猜测。对于客户端 VM,该数字通常较低,但变化不大:介于 1100 和 1650 之间。

此外,最大深度在测试开始时似乎较低,并在测试结束时增加。

于 2013-09-19T14:59:07.987 回答