jvm抛出异常时先查当前栈帧的异常处理表,该表由编译器生成、存于code属性中,按from-to范围、type类型匹配及target跳转执行catch或finally。

当方法执行中抛出异常时,JVM 不会盲目地向上层调用者传递,而是先在当前栈帧的 异常处理表(Exception Table) 中查找匹配的捕获规则。这个表是编译期生成、存储在方法的字节码属性 Code 中的结构,决定了“哪个异常类型、在哪个代码范围内、跳转到哪条指令去处理”。
异常处理表的结构与含义
每条表项包含四个字段:
- from 和 to:表示监控的字节码索引范围(左闭右开),仅当异常发生在该区间内才考虑此条目
-
target:异常处理器的起始字节码索引,即
catch块对应的指令位置 -
type:指向常量池中一个
CONSTANT_Class_info符号引用,代表能被此处理器捕获的异常类(如java/lang/NullPointerException);若为null(即值为 0),表示匹配任意异常(对应catch (Throwable e)或finally)
try-catch-finally 如何映射为异常表项
一个 try-catch-finally 结构通常生成多条异常表项:
- 每个
catch块对应一条表项,type指向其声明的异常类型,from-to覆盖整个try块范围 -
finally块会被编译器“复制”到多个位置:正常执行末尾、每个catch块末尾、以及所有可能提前退出(如return、break)的位置;对应地,会添加若干条type = null的表项,覆盖各监控区域并指向finally入口 - 因此,即使没有显式
catch,只要存在finally,异常表中也会有type = null的项,确保异常传播前先执行finally
异常匹配的查找过程
JVM 抛出异常时,在当前方法的异常表中按顺序扫描:
- 跳过
from >= pc或to 的项(<code>pc是异常发生的字节码位置) - 对剩余项,检查异常对象是否是
type所指类或其子类(包括type == null的情况) - 找到第一条匹配项后,清空操作数栈,将异常对象压入栈顶,然后跳转到
target指令继续执行(即进入catch或finally) - 若遍历完无匹配,则当前方法调用结束,异常沿调用栈向上传播,由上层栈帧重复该过程
常见误区与注意事项
异常表是静态结构,不支持运行时动态注册;它的行为完全由编译器根据源码结构生成:
-
catch子句的异常类型必须是Throwable的子类,否则编译失败——因为异常表中的type必须指向有效类符号 - 空的
catch块(如catch (Exception e) {})仍会生成异常表项,只是后续字节码不处理异常对象 - 多异常捕获(
catch (IOException | SQLException e))在字节码中表现为多条异常表项,分别指向同一target,但type各自不同 - 异常表不记录
throws声明,后者仅用于编译期检查,不影响运行时异常分发逻辑











