athrow 是 JVM 中唯一显式抛出异常的字节码指令,要求操作数栈顶为非 null Throwable 实例,触发后清空当前栈帧并按异常表查找 handler,未找到则沿调用链向上回溯,finally 由编译器插入而非 athrow 触发。

athrow 是 JVM 中唯一用于显式抛出异常的字节码指令,它不负责创建异常对象,而是把已存在的异常引用“扔出去”,触发整个异常分发流程。
athrow 执行前必须满足的条件
执行 athrow 前,操作数栈顶必须是一个非 null 的 Throwable 或其子类实例引用。如果栈顶是 null,JVM 会立即抛出 NullPointerException,而不是你写的那个异常。
- 异常对象通常由 new + invokespecial(调用构造方法)提前创建好,再压入栈顶
- 编译器确保 throw new XxxException() 这类语句最终生成合法的 athrow 指令序列
- 不能直接对任意对象使用 athrow —— 类型检查在字节码验证阶段就已完成
athrow 触发后的核心动作
一旦 athrow 开始执行,JVM 立即终止当前方法的正常执行流,并启动异常查找机制:
- 清空当前方法的操作数栈和局部变量表(栈帧内容被丢弃)
- 从当前方法的异常表(Exception Table)中,按顺序匹配:起始 PC、结束 PC、异常类类型、handler PC
- 找到第一个兼容的 catch 块(例如抛出 RuntimeException,而 catch 块声明捕获 Exception,也算匹配)
- 跳转到 handler PC 对应的字节码位置,把异常对象压入该 catch 块的操作数栈顶,开始执行
找不到 handler 时会发生什么
如果当前方法没有匹配的异常处理器,JVM 会弹出当前栈帧,回到调用者方法,重复上述查找过程:
立即学习“Java免费学习笔记(深入)”;
- 这个过程沿调用链逐层向上,直到某个方法能处理它
- 如果一路回到最外层(如 main 方法)仍无 handler,线程将终止,JVM 输出未捕获异常堆栈
- 注意:finally 块会在每次方法退出前执行(包括因异常退出),但不是靠 athrow 触发,而是编译器插入的 goto 和 jsr/rct 逻辑
容易被忽略的细节
athrow 所在的行号未必是你源码里写的 throw 那一行:
- javap -c -v 可查看 Code 区域,结合 LineNumberTable 找真正对应的源码行
- 内联方法(如 Optional.get)、桥接方法、泛型擦除后,athrow 可能落在 JDK 内部类里
- 性能上,athrow 本身极快,但异常流程涉及栈帧清理、表查找、上下文切换,开销主要在这里


















