为什么有可能从StackOverflowError中恢复?

即使在Java中发生StackOverflowError之后,我仍然可以继续执行,这让我很惊讶。

我知道StackOverflowError是类错误的一个子类。 Error类被解释为“Throwable的一个子类,表示合理的应用程序不应该试图捕捉的严重问题”。

这听起来更像是一个推荐,而不是一个规则,像一个StackOverflowError捕获错误实际上是允许的,这是由程序员的合理性,不这样做。 而且看,我testing了这个代码,它正常结束。

 public class Test { public static void main(String[] args) { try { foo(); } catch (StackOverflowError e) { bar(); } System.out.println("normal termination"); } private static void foo() { System.out.println("foo"); foo(); } private static void bar() { System.out.println("bar"); } } 

怎么会这样? 我想在StackOverflowError抛出的时候,堆栈应该足够大,以致于没有空间来调用另一个函数。 error handling块是否在不同的堆栈中运行,或者在这里发生了什么?

当堆栈溢出并引发StackOverflowError ,通常的exception处理将展开堆栈。 展开堆栈意味着:

  • 中止当前活动函数的执行
  • 删除它的堆栈帧,继续调用函数
  • 中止调用者的执行
  • 删除它的堆栈帧,继续调用函数
  • 等等…

…直到发现exception。 这是正常的(事实上是必要的),并且独立于抛出哪个exception以及为什么。 由于在第一次调用foo()之外捕获exception,填满堆栈的成千上万个foo堆栈已全部解除,大部分堆栈都可以再次使用。

当StackOverflowError被抛出时,堆栈已满。 但是,当它被捕获时 ,所有这些foo调用都从堆栈中popup。 bar可以正常运行,因为堆栈不再与foo s溢出。 (注意,我不认为JLS保证你可以像这样从堆栈溢出中恢复。)

当发生StackOverFlow时,JVM将popup到catch,释放堆栈。

在你的例子中,它摆脱了所有的堆叠foo。

因为堆栈实际上没有溢出。 一个更好的名字可能是AttemptToOverflowStack。 基本上,这意味着最后一次尝试调整堆栈帧错误,因为堆栈上没有足够的可用空间。 实际上堆栈可能有很多空间,只是空间不够。 所以,无论什么操作都依赖于调用的成功(通常是方法调用),从来没有得到执行,剩下的只是程序处理这个事实。 这意味着它与其他任何例外都没有什么不同。 事实上,你可以在正在进行调用的函数中捕获exception。