如何在Java中忽略StackOverflow错误?

huangapple go评论75阅读模式
英文:

How to ignore StackOverFlow error in Java?

问题

我知道为什么会出现错误,我不想去修正它,我希望系统忽略它继续运行。
我知道递归不是无限的,我需要知道它何时会停止,尽管我确信会是一个很大的数字,并且经过相当长的时间执行后会停止。

谢谢。

public static int fun1(int begin, int end, int cont) {
    if (begin >= end) {
        return 1;
    }
    cont += fun1(begin, (begin + end) / 2, cont);
    cont += fun1(begin + (end - begin) / 4, begin + 3 * (end - begin) / 4, cont);
    cont += fun1((begin + end) / 2, end, cont);
    return cont;
}
英文:

I know why it gives the error, I don't want to correct it I want the system to ignore it and keep running.
I know that the recursion is not infinite and I need to know when it will stop although I am sure it will be in a very large number and after a good while of execution.

Thankssss.

public static int fun1(int begin, int end, int cont) {
    if (begin >= end) {
        return 1;
    }
    cont += fun1(begin, (begin + end) / 2, cont);
    cont += fun1(begin + (end - begin) / 4, begin + 3 * (end - begin) / 4, cont);
    cont += fun1((begin + end) / 2, end, cont);
    return cont;
}

答案1

得分: 3

>我知道递归不是无限的

你“知道”一个错误。

public static int fun1(int begin, int end, int cont) {
    if (begin >= end) {
        return 1;
    }
    cont += fun1(begin, (begin + end) / 2, cont);
    cont += fun1(begin + (end - begin) / 4, begin + 3 * (end - begin) / 4, cont);
    cont += fun1((begin + end) / 2, end, cont);
    return cont;
}

考虑一下如果我们调用 fun1(0, 1, 0) 会发生什么。

begin >= end 吗?不,begin == 0 而且 end == 1。所以我们进行递归。

我们递归调用的参数是什么?(begin + end) / 2 等于 (0 + 1) / 2 等于 1 / 2,使用整数除法得到 0。所以第一个递归调用是 fun1(0, 0, 0)

但是最后一个递归调用是 fun1(0, 1, 0)。等等。那看起来很熟悉,对吧?(不要介意中间那个;我们已经展示了一个错误。)

英文:

>I know that the recursion is not infinite

You "know" a falsehood.

public static int fun1(int begin, int end, int cont) {
    if (begin >= end) {
        return 1;
    }
    cont += fun1(begin, (begin + end) / 2, cont);
    cont += fun1(begin + (end - begin) / 4, begin + 3 * (end - begin) / 4, cont);
    cont += fun1((begin + end) / 2, end, cont);
    return cont;
}

Consider what happens if we call fun1(0, 1, 0).

Is begin >= end? No; begin == 0 and end == 1. So we recurse.

What are the arguments for our recursive calls? (begin + end) / 2 is equal to (0 + 1) / 2 is equal to 1 / 2 is equal to 0, with integer division. So the first recursive call is fun1(0, 0, 0).

But the last recursive call is fun1(0, 1, 0). Wait. That looks familiar, yeah? (Never mind the middle one; we have already shown a fault.)

答案2

得分: 1

@Karl's answer解释了为什么你的函数实际上是无限递归的。

但是你问道:

> 如何忽略Java中的StackOverflow错误?

你不能简单地忽略它。如果这样做,程序会崩溃(或者一个子线程会终止,或者其他情况),你将得不到答案。

你可以这样做:

Integer result = null;
try {
    result = fun1(x, y, z);
} catch (StackOverflowError e) {
    // 我们在这里忽略了这个异常
}

if (result == null) {
    System.out.println("无法计算 fun1(x, y, z)");
} else {
    System.out.println("fun1(x, y, z) 为 " + result);
}

但是当你看这段代码时,实际上我们并没有真正地忽略异常。我们捕获了它,然后(最终)将其作为一种特殊情况进行处理。


请注意,即使在递归不是无限的情况下,你也可能会遇到 StackOverflowError。例如,如果我使用递归来相加两个数:

public int add(int a, int b) {
    if (a < 0 || b < 0) {
        throw new IllegalArgumentException("负数");
    } else if (a == 0) {
        return b;
    } else {
        return add(a - 1, b + 1);
    }
}

由于Java(通常情况下)不执行尾递归优化,足够大的参数会导致 StackOverflowError


那么在函数不是无限递归的情况下,如何解决 StackOverflowError 呢?

  • 一种方法是使用更大的线程堆栈大小。可以通过使用 -Xss<value> 选项来设置JVM的默认堆栈大小。或者,在创建新线程时可以通过 Thread 构造函数提供堆栈大小。然而,实际可行的线程堆栈大小受可用内存量的限制,还有可能受到JVM、操作系统和硬件架构的限制。

  • 第二种方法是将递归函数转换为迭代形式。但要注意,如果只是使用软件中实现的堆栈来模拟递归调用,就必须处理数据结构可能变得过大的情况。

  • 第三种方法是尝试找到代数解;即将问题转化为数学问题。

但要注意,有些情况下上述方法都不起作用。例如,考虑一下阿克曼函数

英文:

@Karl's answer explains why your function is actually infinitely recursive.

But you asked:

> How to ignore StackOverFlow error in Java?

You cannot simply ignore it. If you do that, the program crashes (or a child thread dies, or something) and you don't get an answer.

You could do this:

Integer result = null;
try {
    result = fun1(x, y, z);
} catch (StackOverflowError e) {
    // We are ignoring this
}

if (result == null) {
    System.out.println(&quot;cannot compute fun1(x,y,z)&quot;);
} else {
    System.out.println(&quot;fun1(x,y,z) is &quot; + result);
}

But when you look at this, we are not really ignoring the exception. We are catching it and (ultimately) dealing with it as a special case.


Note that you can get a StackOverflowError even in cases where the recursion is not infinite. For example, if I was to add two numbers using recursion:

public int add(int a, int b):
   if (a &lt; 0 || b &lt; 0) {
       throw new IllegalArgumentException(&quot;negative&quot;);
   else if (a == 0) {
       return b;
   } else
       return add(a - 1, b + 1);
   }

Since Java (typically) doesn't do tail-call optimization, large enough arguments will give a StackOverflowError.


So what it the solution to StackOverflowErrors in cases where the function is not infinitely recursive?

  • One approach would be to use a larger thread stack size. This could be done by using the -Xss&lt;value&gt; option to set the default stacksize for the JVM. Alternatively, you can supply a stack size via the Thread constructor when creating a new thread. However, the maximum practical thread stack size is limited by the amount of available memory, and potentially by JVM, OS and hardware architectural limits.

  • A second approach is to translate the recursive function into an iterative form. But note that if you simply simulate the recursive calls using a stack implemented in software, you have to deal with the possibility that that data structure gets too big.

  • A third approach is to try and find an algebraic solution; i.e. turn this into a mathematical problem.

But note that there are some cases where none of the above will work. For example, consider the Ackermann function.

huangapple
  • 本文由 发表于 2020年9月19日 07:16:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/63963843.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定