当前位置: 首页 > article >正文

Java中的try-catch在jvm层面是怎么做的?

简单描述

java中的try-catch通过异常表栈展开来实现

异常表(exception-table)

每个方法的字节码中都有一个异常表,用于记录try-catch块的作用范围对应的异常处理逻辑

异常表的每个条目包含以下信息:

起点,终点,处理代码的位置,捕获异常的类型

起点(start_pc):try块的起始指令偏移量

终点(end_pc):try块的结束指令偏移量(不包含该指令)

处理代码位置(handler_pc):catch块的第一条指令偏移量

捕获的异常类型(catch_type):要捕获的异常类(如java/lang/Exception),若为0表示捕获所有异常(finally块)

字节码结构

Exception table:
   start  end  handler  type
   0      10   13       java/io/IOException
   0      10   20       java/lang/Exception

异常处理流程

抛出异常,从异常表判断异常是否在处理逻辑内(也就是是否被try-catch{}包围),

代码中抛出异常时,JVM会执行以下步骤:

创建异常对象:实例化抛出的异常(如new IOException()

查找异常表:从当前方法的异常表中,按顺序匹配以下条件:

异常抛出的位置是否在某个条目的[start_pc, end_pc)范围内。

抛出的异常是否是catch_type的子类(或自身)

跳转到处理代码

若找到匹配条目,跳转到handler_pc执行catch

若未找到,触发栈展开:弹出当前栈帧,回到调用者方法重复上述过程。栈展开确保异常沿调用链向上传播,直到被处理或终止线程

未捕获异常:若所有栈帧均未处理异常,线程终止并打印堆栈跟踪


finally块的实现

finally 块的核心是:无论 try 或 catch 块中是否抛出异常或提前返回,finally 中的代码必须执行

为了实现这一点,JVM 的编译器(如 javac)在生成字节码时,会通过两种机制来确保 finally 的执行

finally块通过两种方式实现:

代码复制:编译器将finally代码复制到trycatch块的所有退出路径(包括return或异常抛出之后)。

异常表条目兜底:若finally需要处理异常退出,会生成一个catch_type=0的条目,捕获所有异常并执行finally代码,之后重新抛出异常


代码复制

编译器会将 finally 块中的代码复制到所有可能的退出路径,包括:

try 块正常结束后的退出路径。

catch 块处理完异常后的退出路径。

try 或 catch 块中的 return、break、continue 语句之前

java代码

public void example() {
    try {
        System.out.println("try");
    } catch (Exception e) {
        System.out.println("catch");
    } finally {
        System.out.println("finally");
    }
}

编译后的字节码逻辑

// try 块
L0:
    System.out.println("try");
    // 复制 finally 代码到 try 块末尾
    System.out.println("finally");
    return;

// catch 块
L1:
    System.out.println("catch");
    // 复制 finally 代码到 catch 块末尾
    System.out.println("finally");
    return;

// 异常表条目(自动处理异常后的 finally)
Exception table:
    start=L0, end=L0, handler=L1, type=Exception

关键点:

finally 的代码会被复制到 try 和 catch 的末尾,确保正常流程下一定会执行

如果 try 或 catch 中有 return,编译器会先执行 finally 代码,再执行 return


异常表兜底(处理未捕获的异常)

如果 try 或 catch 块中抛出了未被捕获的异常,或者有 throw 语句,JVM 会通过异常表跳转到 finally 代码,执行后再重新抛出异常。

异常表条目

编译器会生成一个特殊的异常表条目用于捕获所有类型的异常(catch_type=0)

确保任何未处理的异常都会先执行 finally,再继续传播异常

java代码

public void example() {
    try {
        throw new IOException();
    } finally {
        System.out.println("finally");
    }
}

字节码的异常表会生成如下头目

Exception table:
    start=L0, end=L1, handler=L2, type=0  // type=0 表示捕获所有异常

对应的执行流程:

  1. try 块抛出 IOException
  2. JVM 查找异常表,发现 type=0 的条目(匹配所有异常)。
  3. 跳转到 handler=L2finally 代码的位置)执行 System.out.println("finally")
  4. 重新抛出异常,继续栈展开

假设代码中有 try 和 finally,但没有 catch:

try {
    throw new Exception();
} finally {
    System.out.println("finally");
}

执行步骤:

  1. try 块抛出异常,JVM 创建异常对象。
  2. 直接查找当前方法的异常表,找到 catch_type=0 的条目,跳转到 finally 代码。
  3. 执行 finally 块中的代码。
  4. 重新抛出异常,由外层调用者处理


http://www.kler.cn/a/584601.html

相关文章:

  • Linux笔记---文件系统硬件部分
  • Selenium 中的 alert 处理
  • MyBatis-Plus 分页查询接口返回值问题剖析
  • C++ 模板初阶总结
  • QT系列教程(16) 定时器事件
  • 为什么 Young GC 比 Full GC 快
  • 科技创新:改变生活的力量与未来趋势
  • nginx学习,URI,try_files
  • AWS Bedrock全托管接入国产大模型DeepSeek-R1[内涵免费使用DeepSeek-R1满血版]
  • 微博发布Q4及全年财报:微博成汽车手机新品营销主阵地
  • 【后端】【django】导出 API 文档的几种方法
  • 网络安全相关资源安全合规检查与整改计划‌
  • 【开源+代码解读】Search-R1:基于强化学习的检索增强大语言模型框架3小时即可打造个人AI-search
  • vue2双向绑定解析
  • 【 <一> 炼丹初探:JavaWeb 的起源与基础】之 JavaWeb 中的文件上传与下载:实现文件管理功能
  • STM32 HAL库实战:轻松实现串口通信驱动蓝牙模块与ESP8266开发
  • 数据中心安全建设方案,数据安全运营体系建设方案,信息中心安全解决方案(PPT)
  • 深度学习基础--CNN经典网络之“DenseNet”简介,源码研究与复现(pytorch)
  • RabbitMQ五种消息模型
  • 零成本搭建Calibre个人数字图书馆支持EPUB MOBI格式远程直读