JVM 如何处理异常?try-catch-finally 结构在字节码层面是如何实现的?
# 1. JVM 处理异常的核心机制
JVM 通过 “异常表(Exception Table)” 和 “异常处理器(Exception Handler)” 处理异常,核心流程如下:
- 当 Java 代码抛出异常(
throw new Exception())或执行时出现错误(如空指针、数组越界),JVM 会创建Throwable对象(异常实例); - JVM 遍历当前方法的 “异常表”,查找能处理该异常的 “异常处理器”(匹配异常类型和代码范围);
- 若找到匹配的处理器,JVM 跳转到处理器对应的字节码地址执行(如
catch块); - 若未找到,JVM 将异常向上抛给调用者方法,重复上述流程;若所有方法均未处理,JVM 终止程序,输出异常堆栈信息。
# 2. try-catch-finally 在字节码层面的实现
以以下代码为例,分析字节码层面的实现逻辑:
java
运行
public class ExceptionDemo {
public static void main(String[] args) {
try {
int a = 1 / 0; // 可能抛出ArithmeticException
} catch (ArithmeticException e) {
System.out.println("捕获算术异常");
} finally {
System.out.println("执行finally块");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# (1)异常表(Exception Table)的生成
编译后,字节码中会生成 “异常表”,记录try块的代码范围、异常类型、catch块的入口地址,示例如下(通过javap -v ExceptionDemo.class查看):
plaintext
Exception table:
from to target type
0 10 13 Class java/lang/ArithmeticException
1
2
3
2
3
from/to:try块的字节码起始地址(0)和结束地址(10);target:catch块的字节码起始地址(13);type:能处理的异常类型(ArithmeticException)。
# (2)try 块的执行
- 字节码中,
try块的代码(int a = 1 / 0)正常执行; - 若执行过程中抛出
ArithmeticException,JVM 查询异常表,发现该异常在from-to范围内且类型匹配,跳转到target地址(13)执行catch块。
# (3)catch 块的执行
catch块的字节码逻辑:创建ArithmeticException对象(e),执行System.out.println("捕获算术异常");- 执行完成后,JVM 会跳转到
finally块的入口地址,确保finally块执行。
# (4)finally 块的强制执行
finally块的核心特性是 “无论try块是否抛出异常,finally块都会执行”,字节码层面通过以下两种方式实现:
- 方式 1:正常执行路径:若
try块未抛出异常,执行完try块后,JVM 会插入跳转指令,强制跳转到finally块; - 方式 2:异常执行路径:若
try块抛出异常并被catch块处理,执行完catch块后,JVM 同样插入跳转指令,跳转到finally块; - 特殊情况:try/catch 块中执行 return:若
try块或catch块中有return语句,JVM 会先将return的返回值存入临时变量,再执行finally块,最后从临时变量中取出返回值执行return(确保finally块执行后再返回)。
# (5)未捕获异常的处理
若try块抛出的异常未被任何catch块处理(如抛出NullPointerException,但catch块只处理ArithmeticException),JVM 会:
- 执行
finally块(若存在); - 将异常向上抛给调用者方法(
main方法的调用者是 JVM); - JVM 调用
Thread.dispatchUncaughtException(),输出异常堆栈信息,终止程序。
上次更新: 12/30/2025