首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | Java代码是怎么运行的?
02 | Java的基本类型
03 | Java虚拟机是如何加载Java类的?
04 | JVM是如何执行方法调用的?(上)
05 | JVM是如何执行方法调用的?(下)
06 | JVM是如何处理异常的?
07 | JVM是如何实现反射的?
08 | JVM是怎么实现invokedynamic的?(上)
09 | JVM是怎么实现invokedynamic的?(下)
10 | Java对象的内存布局
11 | 垃圾回收(上)
12 | 垃圾回收(下)
13 | Java内存模型
14 | Java虚拟机是怎么实现synchronized的?
15 | Java语法糖与Java编译器
16 | 即时编译(上)
17 | 即时编译(下)
18 | 即时编译器的中间表达形式
19 | Java字节码(基础篇)
20 | 方法内联(上)
21 | 方法内联(下)
22 | HotSpot虚拟机的intrinsic
23 | 逃逸分析
24 | 字段访问相关优化
25 | 循环优化
26 | 向量化
27 | 注解处理器
28 | 基准测试框架JMH(上)
29 | 基准测试框架JMH(下)
30 | Java虚拟机的监控及诊断工具(命令行篇)
31 | Java虚拟机的监控及诊断工具(GUI篇)
32 | JNI的运行机制
33 | Java Agent与字节码注入
34 | Graal:用Java编译Java
35 | Truffle:语言实现框架
36 | SubstrateVM:AOT编译框架
当前位置:
首页>>
技术小册>>
深入拆解 Java 虚拟机
小册名称:深入拆解 Java 虚拟机
### 06 | JVM是如何处理异常的? 在Java编程中,异常处理是确保程序健壮性和错误恢复的关键机制之一。Java虚拟机(JVM)作为Java程序的运行环境,承担着执行Java字节码并管理程序生命周期的重要职责,其中就包括了异常的处理。本章将深入探讨JVM如何处理Java中的异常,从异常的产生、捕获、传播到最终的处理,全面解析这一过程。 #### 一、异常的基本概念 在Java中,异常(Exception)是一种特殊的对象,它表示程序执行过程中发生的异常情况。Java异常体系基于继承关系,所有异常类都是`Throwable`类的子类,其中`Error`和`Exception`是`Throwable`的直接子类。`Error`通常表示系统级的错误,如内存溢出(`OutOfMemoryError`),这类错误通常无法被程序捕获处理;而`Exception`及其子类则用于表示可由程序捕获并处理的异常情况。`Exception`又进一步分为`Checked Exception`(必须被显式捕获或声明的异常)和`Unchecked Exception`(运行时异常,如`NullPointerException`,通常不被强制要求捕获)。 #### 二、异常的抛出与捕获 ##### 2.1 异常的抛出 当Java程序执行过程中遇到无法继续执行的情况时,会创建一个异常对象,并通过`throw`关键字抛出。这个异常对象随后会被JVM捕获并处理。异常可以在方法内部抛出,并由调用该方法的代码捕获处理,或者继续向上层传播,直至被合适的异常处理器捕获。 ```java public void testException() { throw new IllegalArgumentException("参数不合法"); } ``` ##### 2.2 异常的捕获 Java通过`try-catch-finally`语句块来捕获并处理异常。`try`块中放置可能抛出异常的代码,`catch`块用于捕获并处理异常,而`finally`块(可选)无论是否发生异常都会执行,常用于资源释放等清理工作。 ```java try { // 可能抛出异常的代码 testException(); } catch (IllegalArgumentException e) { // 处理IllegalArgumentException System.out.println("捕获到异常:" + e.getMessage()); } finally { // 清理代码 } ``` #### 三、JVM对异常的处理机制 JVM对异常的处理主要发生在字节码执行层面,它遵循Java语言规范中定义的异常处理规则。 ##### 3.1 异常表的构建 在Java编译过程中,编译器会分析源代码中的异常处理结构,并生成相应的异常表(Exception Table)作为类文件的一部分。异常表记录了每个`try`块及其对应的`catch`块的信息,以及`finally`块(如果有的话)的位置信息。这些信息用于在字节码执行过程中,当异常发生时,JVM能够快速定位到相应的异常处理器。 ##### 3.2 异常传播与查找 当JVM执行到某条字节码指令导致异常时,它会首先检查当前执行点是否在某个`try`块内。如果是,JVM会根据异常表的信息,查找匹配的`catch`块。匹配过程基于异常类型进行,JVM会查找能够处理当前异常或其父类异常的最近的`catch`块。如果找到,则跳转到该`catch`块的开始执行;如果未找到,则异常会继续向上传播至调用者,直到找到匹配的`catch`块或到达方法边界(此时JVM会向调用者抛出异常)。 ##### 3.3 finally块的执行 无论是否发生异常,`finally`块(如果存在)都会在`try`或`catch`块之后执行(如果在`try`块中遇到`return`、`break`或`continue`等控制流语句,`finally`块也会在执行这些语句之前执行)。这一机制确保了资源的释放和清理工作得以执行,增强了程序的健壮性。 #### 四、JVM中的异常链与异常封装 在异常处理过程中,有时一个异常的处理可能会引发另一个异常。为了保留原始异常的信息,Java允许将原始异常封装在新的异常中,形成异常链。在JVM层面,这通过异常的`cause`机制实现,即异常对象可以携带一个`Throwable`类型的`cause`属性,用于记录原始异常。 ```java try { // 可能抛出异常的代码 } catch (IOException e) { throw new RuntimeException("操作失败", e); } ``` 在上述代码中,如果`IOException`被捕获,则会创建一个新的`RuntimeException`,并将`IOException`作为其原因(cause)封装进去。这样,在异常处理链的后续环节中,仍然可以访问到原始的`IOException`信息。 #### 五、JVM对未捕获异常的处理 如果异常没有被任何`catch`块捕获,并且也没有通过方法签名声明抛出(对于`Checked Exception`),那么JVM会将该异常视为未捕获的异常。此时,JVM会根据异常的类型和当前线程的上下文(如是否在主线程中执行)来决定如何处理该异常。通常,JVM会打印出异常的堆栈跟踪信息到标准错误输出(如控制台),并终止当前线程的执行。对于主线程(通常是启动程序的线程),这会导致整个程序的终止。 #### 六、JVM与异常性能 虽然异常处理是Java语言的一个重要特性,但它也可能对程序性能产生一定影响。每次异常发生时,JVM都需要构建异常对象、填充堆栈跟踪信息,并在异常表中查找匹配的`catch`块,这些操作都需要消耗一定的时间和资源。因此,在性能敏感的代码段中,应避免不必要的异常抛出和捕获,特别是在循环或高频调用的方法中。 #### 七、总结 JVM对Java异常的处理是一个复杂而精细的过程,它涉及到异常表的构建、异常传播与查找、`finally`块的执行、异常链与封装以及未捕获异常的处理等多个方面。了解和掌握JVM的异常处理机制,对于编写健壮、可维护的Java程序至关重要。在实际开发中,我们应合理利用异常处理机制,既要确保程序能够优雅地处理异常情况,又要避免不必要的性能开销。
上一篇:
05 | JVM是如何执行方法调用的?(下)
下一篇:
07 | JVM是如何实现反射的?
该分类下的相关小册推荐:
Java必知必会-Maven高级
SpringBoot合辑-初级篇
JAVA 函数式编程入门与实践
Java语言基础6-面向对象高级
Java语言基础5-面向对象初级
Java语言基础13-类的加载和反射
Java语言基础14-枚举和注解
Java必知必会-Maven初级
Mybatis合辑2-Mybatis映射文件
经典设计模式Java版
Java语言基础7-Java中的异常
Java面试指南