java 什么时候发生StackOverflowError?




recursion stack-overflow (4)

这个问题在这里已经有了答案:

根据Oracle,一个StackOverflowError是:

由于应用程序递归太深而发生堆栈溢出时抛出。

我知道递归是什么,通常递归函数,如果没有正确终止,导致StackOverflowError。 为了检查在引发StackOverflowError之前发生的递归调用的次数,我编写了下面的代码:

package ErrorCases;

public class StackOverFlowError {
static int i=0;
void a()
{

    //System.out.println("called "+(++i));
    try{
        ++i;
    a();
    }catch(Error e)
    {
        System.out.println(e.getClass());
        System.out.println(i);
    }
}

public static void main(String[] args) {

       new StackOverFlowError().a();

   }

}

在JVM抛出StackOverflowError之前, i的值给了a()的递归调用的计数。
i的价值在每次运行中都是不同的,例如:

output 1: class java.lang.StackOverflowError
           10466
Output 2: class java.lang.StackOverflowError
           10470

我的查询是?

  1. 在JVM抛出StackOverflowError之前递归有多深?

  2. 我们可以恢复一旦StackOverflowError已被抛出?


深度取决于两件事情:

1:堆栈的大小。

2:每个递归中使用的堆栈空间量。

函数参数,局部变量和返回地址都分配在堆栈上,而对象则分配在堆上。

复苏

有可能恢复。

try {
    myDeepRecursion();
} catch (Error e) {
  // We are back from deep recursion. Stack should be ok again.
}

但是,请注意以下有关错误(来自java API文档):

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.

编辑:

请注意:在递归函数中捕获异常是可以的,但不要试图捕获错误。 如果堆栈已满,则错误处理将导致新的错误。 简单的事情,例如对System.out.println()的调用将会失败,因为返回地址没有剩余空间。

这就是为什么错误应该被捕获到递归函数之外的原因。


在JVM抛出Error之前递归有多深?

这真的取决于你正在使用的机器,你的JVM及其配置。 (在我当前的设置中是6000-8500,后台有很多任务),最多也就是你在应用程序和递归方法中所做的。

我们可以恢复一旦Error已被抛出?

没有! 我Java抛出一个错误,没有办法恢复正常。 这是Exceptions和Errors之间的主要区别。

一个错误是Throwable的一个子类,表示一个合理的应用程序不应该试图捕捉的严重问题

阅读更多关于错误的信息

在这里阅读更多关于Java堆栈大小的神话

编辑:

发生错误后,仍然可以做些事情,但这样做有意义吗? 你的代码有一个严重的错误! 你不能确定一切正常!

你的代码没有终止,因为你再次调用方法,就像无尽的循环一样


您可能会看到不同的堆栈深度的原因是堆栈帧不一定是相同的大小。 例如,热点JVM具有用于JIT编译代码和解释代码的不同栈帧。 JIT编译器与正在运行的代码并行工作,因此外部因素(如机器负载)可能会影响JVM何时/如果JIT堆栈帧开始使用。

你应该记住的一件事情是,当你得到一个Error ,几乎所有你做的事情都可能导致另一个Error 。 例如,在catch部分中打印错误类的名称也可能会用完堆栈空间,因此另一个Error将被抛出堆栈。

有些人声称,由于JVM正在关闭,您无法从Error恢复。 这是不正确的; 你可以从一些错误中恢复。 有关ErrorOutOfMemoryError的棘手问题是,一旦你捕获了一个,你完全不能保证程序的状态。 例如,您使用的关键数据结构可能处于不一致的状态。 这使得恢复很难或不可能。


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

你的过程也有一堆,生活在你的过程的底端。 当你分配内存时,这个堆可以向你的地址空间的上端增长。 正如你所看到的,堆有可能与堆栈“碰撞”(有点像techdonic板!!!)。 [资源]

所以StackOIverflowError将取决于您的堆栈大小以及堆大小,这将取决于从执行到执行,因为很多因素,如GC。

也正如名称所说,它是错误,而不是例外。 没有从它的恢复。 JVM将关闭。





stack-overflow