1. 异常机制简介
- Java 通过异常机制来处理程序运行时的错误,分为检查型异常(Checked Exception)和非检查型异常(Unchecked Exception)。
- 异常处理依赖
try-catch-finally
语句块。
2. Java 字节码中的异常处理结构
- 在编译后的
.class
文件中,异常处理信息存储在 异常表(Exception Table) 中。
- 异常表记录了异常处理的起始和结束代码范围,以及对应的处理代码块入口(catch块)。
2.1 异常表结构
异常表是每个方法的属性之一,记录条目格式包括:
字段 | 说明 |
---|
start_pc | 监控异常的代码块开始指令索引 |
end_pc | 监控异常的代码块结束指令索引 |
handler_pc | 异常处理代码块入口指令索引 |
catch_type | 捕获的异常类型常量池索引(0表示finally) |
3. 异常抛出与捕获流程(字节码层)
3.1 抛出异常
- Java 代码执行时,遇到
throw new Exception()
,JVM 创建异常对象并开始异常处理流程。
- 字节码使用
athrow
指令抛出异常对象。
3.2 查找异常处理器
- JVM从当前执行指令的异常表开始,查找能处理当前异常类型的
catch
块。
- 查找条件:异常发生指令的PC地址在异常表的
[start_pc, end_pc)
范围内,且异常类型匹配(继承关系支持)。
3.3 找到处理器
- 如果找到匹配的处理器,则跳转到
handler_pc
指令执行 catch
代码。
- 如果没有,调用栈回退,逐层查找调用方法的异常处理器。
3.4 finally 处理
finally
块在字节码中通常实现为异常表中 catch_type=0
的处理器,保证无论是否异常都能执行。
4. 关键字节码指令示例
指令 | 说明 |
---|
athrow | 抛出异常 |
try-catch | 通过异常表映射处理代码 |
goto | 跳转指令,catch跳转使用 |
5. 例子:简单异常代码与对应字节码结构
5.1 Java代码
public void example() {
try {
int a = 1 / 0;
} catch (ArithmeticException e) {
System.out.println("Exception caught");
}
}
5.2 编译后的异常表伪代码
start_pc | end_pc | handler_pc | catch_type |
---|
0 | 10 | 11 | ArithmeticException 常量池索引 |
- 当执行除零异常,抛出异常,JVM查看异常表,发现异常位于
[0,10)
,异常类型匹配,跳转到 handler_pc=11
执行catch。
6. JVM异常处理特点
- 异常处理是基于 表驱动(Exception Table),而非线性搜索。
- 异常表存在于
.class
文件的 Code 属性中,保证运行时高效查找。
- 异常抛出会产生性能开销,非异常路径性能不受影响。
7. 小结
关键点 | 说明 |
---|
异常抛出指令 athrow | JVM抛出异常对象,触发异常处理流程 |
异常表定位处理代码 | 方法内异常表定义异常范围及处理代码入口 |
继承支持异常匹配 | 支持捕获异常及其子类 |
栈回退查找异常处理器 | 异常未捕获则传递至调用者继续查找 |
finally实现方式 | 特殊异常表条目保证执行 |
发表回复