JVM中catch块匹配依赖异常表线性扫描:先判断异常PC是否在[from, to)范围内,再用instanceof检查异常类型是否兼容type字段指向的类,二者同时满足即跳转target执行,顺序决定捕获结果。

在 JVM 字节码中,catch 块的匹配不是靠“类型相等”完成的,而是通过 target PC 范围 + 异常类型继承关系 两个条件共同决定的。JVM 在抛出异常时,会按 ExceptionTable 中的顺序逐条检查:当前异常对象是否落在某条表项的字节码偏移范围内(from ≤ 异常发生 PC < to),且该异常类或其父类是否与表项中的 catch_type 类型兼容。
ExceptionTable 结构决定匹配逻辑
JVM 方法的每个 Code 属性都附带一张异常表(ExceptionTable),每行包含四个字段:
- from:起始 PC(含)
-
to:结束 PC(不含)——即
[from, to)是受保护的字节码范围 -
target:处理该异常的字节码起始 PC(即
catch块第一条指令位置) -
type:捕获的异常类型符号引用(指向常量池中一个
CONSTANT_Class_info);若为 0,表示捕获所有异常(对应catch (Throwable e))
注意:type 不是运行时类对象,而是编译期确定的类型符号;JVM 加载时解析为类,运行时用 instanceof 语义判断兼容性(即:异常实例是否是 type 指向类的实例或其子类)。
匹配过程是线性扫描 + 继承检查
当某条指令抛出异常时,JVM 从异常表第一行开始遍历:
立即学习“Java免费学习笔记(深入)”;
- 检查当前 PC 是否满足
from ≤ pc < to - 若满足,再检查抛出的异常对象是否是
type所指类的实例(含子类) - 一旦找到第一条同时满足两个条件的表项,就跳转到其
targetPC 开始执行catch块 - 找不到则向上层调用栈传播
例如:字节码中 iconst_1 / invokevirtual 在 PC=12 抛出 NullPointerException,而异常表有一项 from=8, to=15, target=20, type=java/lang/RuntimeException,则匹配成功(因为 NPE 是 RuntimeException 子类,且 12 ∈ [8,15)),控制流跳至 PC=20。
多个 catch 的顺序影响实际捕获结果
Java 源码中多个 catch 块会被编译为多条异常表项,**顺序严格保留源码声明顺序**。JVM 不做类型优先级排序,只按表中顺序匹配第一条满足条件的项。
- 所以
catch (IOException e)必须写在catch (Exception e)前面,否则后者会提前拦截所有子类异常 - 字节码层面没有“更具体类型优先”的机制,全靠表项排列顺序
- 编译器会确保子类型表项排在父类型前面,但手动修改字节码时若打乱顺序,行为将改变
type=0 表示通用捕获,需谨慎使用
当 type 字段为 0(常量池索引 0),表示该表项匹配任意异常类型(类似 catch(Throwable))。它仍需满足 PC 范围条件。
- 常用于 finally 或编译器生成的隐式保护块(如资源自动关闭)
- 若同时存在
type=0和其他非零type的表项,且 PC 范围重叠,则排在前面的先被匹配 - 不能仅靠
type=0判断是否是用户写的catch (Throwable),还需结合源码和调试信息


















