抛受检异常与非受检异常的运行时性能无实质差异,开销均来自异常对象构建和栈轨迹填充;所谓“更慢”实为编译期强制处理带来的间接成本,如冗余try-catch、防御性包装或空catch。

性能差异本身并不真实存在——至少不是你直觉中那种“抛受检异常比非受检慢”的差别。
异常抛出开销与类型无关
Java 中所有异常(无论 checked 还是 unchecked)在被 实际抛出并传播 时,JVM 的开销是一致的:都需要构建异常对象、填充栈轨迹(stack trace)、执行异常表跳转。这个过程耗时主要来自 fillInStackTrace() 和栈展开,和它是不是 RuntimeException 子类毫无关系。
也就是说:
– throw new IOException();
– throw new NullPointerException();
这两行代码在运行时的性能成本基本相同。
真正的“性能影响”来自编译期强制逻辑
所谓“受检异常更慢”,其实是开发层面的间接成本,体现在:
- 调用方必须显式处理(try-catch 或 throws),增加代码量和分支路径,可能干扰 JIT 编译器对热点路径的内联与优化
- 为满足编译要求而做的“防御性包装”,比如把
IOException转成RuntimeException再抛出,多一次构造和封装,反而略增开销 - 频繁吞掉受检异常(空 catch)或仅打印日志后忽略,掩盖问题且无实际恢复动作,属于设计缺陷,不是类型本身的锅
关键结论:选异常类型不是为了性能,而是语义清晰
要不要用受检异常,取决于你是否希望调用方不得不面对这个错误场景,比如:
- 文件不存在(
FileNotFoundException)→ 外部依赖失败,调用者理应知道并决定重试/降级/提示用户 - 参数为空(
IllegalArgumentException)→ 属于调用方逻辑错误,应提前校验,不该靠异常兜底
强行把本该 checked 的 I/O 异常改成 unchecked,不会提速;但会让 API 合约模糊、错误被静默忽略,最终导致更难排查的问题——那才是真正的“性能杀手”。


















