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实现方式 | 特殊异常表条目保证执行 |
发表回复