You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JVM中tryItOut抛出StackOverflowError时finally块执行机制探究

问题背景

Java方法代码

void tryCatchFinally() {
    try {
        tryItOut();
    } catch (TestExc e) {
        handleExc(e);
    } finally {
        wrapItUp();
    }
}

对应的JVM字节码

Method void tryCatchFinally()                                                                 
0   aload_0             // Beginning of try block
1   invokevirtual #4    // Method Example.tryItOut()V
4   goto 16             // Jump to finally block
7   astore_3            // Beginning of handler for TestExc;
                    // Store thrown value in local var 3
8   aload_0             // Push this
9   aload_3             // Push thrown value
10  invokevirtual #6    // Invoke handler method:
                    // Example.handleExc(LTestExc;)V
13  goto 16             // This goto is unnecessary, but was
                    // generated by javac in JDK 1.0.2
16  jsr 26              // Call finally block
19  return              // Return after handling TestExc
20  astore_1            // Beginning of handler for exceptions
                    // other than TestExc, or exceptions
                    // thrown while handling TestExc
21  jsr 26              // Call finally block
24  aload_1             // Push thrown value...
25  athrow              // ...and rethrow value to the invoker
26  astore_2            // Beginning of finally block
27  aload_0             // Push this
28  invokevirtual #5    // Method Example.wrapItUp()V
31  ret 2               // Return from finally block
Exception table:
From    To      Target      Type
0       4       7           Class TestExc
0       16      20          any

技术疑问

当try块中的tryItOut()方法抛出StackOverflowError时,此时内存已耗尽,JVM是如何执行finally块并完成后续流程的?


回答

1. 异常匹配与处理入口

StackOverflowError属于Error范畴,不受catch(TestExc e)的捕获,会直接匹配字节码异常表中Type为any的条目(对应目标位置20),这是JVM处理未捕获Error的标准路径。

2. 栈耗尽时的finally执行保障

JVM规范强制要求:无论try块正常完成还是因异常/Error终止,finally块必须执行(除非JVM直接崩溃)。即使触发StackOverflowError导致栈空间耗尽,JVM会先执行栈展开操作——销毁tryItOut()的栈帧,释放部分栈空间,为执行finally块腾出必要的运行资源。

具体执行流程:

  • 跳转到位置20后,执行astore_1:将StackOverflowError实例存入本地变量1(本地变量表空间在方法进入时已分配,无需额外栈空间)。
  • 执行jsr 26:跳转到finally块起始位置26,此时JVM已通过栈展开腾出了执行finally逻辑的基础栈空间。
  • 进入finally块后,依次执行astore_2(存储返回地址到本地变量2)、aload_0、invokevirtual #5调用wrapItUp()——只要wrapItUp()本身是无递归、低栈消耗的简单方法,就能顺利执行完成。
  • 执行ret 2回到jsr后的位置24,最后通过aload_1和athrow将StackOverflowError重新抛出给上层调用者。

3. 极端场景:finally块再次触发栈溢出

如果wrapItUp()内部有递归调用,或者执行时仍需大量栈空间,会在finally块中再次抛出StackOverflowError。此时JVM会终止当前方法执行,直接抛出新的Error,原有的StackOverflowError会被丢弃(成为未被记录的抑制异常)。

4. 内存耗尽的边界说明

这里的“内存耗尽”如果指堆内存耗尽(OutOfMemoryError),属于另一类场景;但针对StackOverflowError导致的栈空间耗尽,JVM通过栈展开机制保障finally块的执行,这是规范硬性要求,除非JVM本身因严重故障无法继续运行。


内容的提问来源于stack exchange,提问作者bigci1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 20:10:44