网站首页 > 精选教程 正文
JVM异常处理机制
异常处理的如下要素共同实现程序控制流的非正常转移:
- 抛出异常
- 捕获异常
抛出异常可分为:
- 显式显式抛异常的主体是应用程序,它指的是在程序中使用“throw”关键字,手动将异常实例抛出。
- 隐式隐式抛异常的主体则是JVM,指JVM在执行过程中,碰到无法继续执行的异常状态,自动抛异常。如JVM在执行读取数组操作时,发现输入的索引值是负数,抛ArrayIndexOutOfBoundsException。
捕获异常涉及如下代码块:
- try代码块标记需异常监控的代码
- catch代码块跟在try代码块之后,用来捕获在try代码块中触发的某种指定类型的异常。除了声明所捕获异常的类型之外,catch代码块还定义了针对该异常类型的异常处理器。在Java中,try代码块后面可以跟着多个catch代码块,来捕获不同类型的异常。Java虚拟机会从上至下匹配异常处理器。因此,前面的catch代码块所捕获的异常类型不能覆盖后边的,否则编译器会报错。
- finally代码块跟在try代码块和catch代码块后,声明一段必运行的代码。为避免跳过某些关键的清理代码,如关闭已打开的系统资源。程序正常执行的情况下,这段代码会在try代码块后运行。否则,try代码块触发异常时,若该异常未被catch,finally块会直接运行,且在运行后重新抛该异常。
- 若该异常被catch代码块捕获,finally代码块则在catch代码块之后运行
- 若不幸的catch代码块也触发了异常,finally代码块同样会运行,并抛出catch代码块所触发异常
- 再极端点,finally代码块也触发异常,则只好中断当前finally代码块的执行,并往外抛异常。
1 异常的基本概念
Java中所有异常都是Throwable类或其子类实例。Throwable有两大直接子类:
- Error,程序不应捕获的异常当程序触发Error,其执行状态已无法恢复,需中止线程甚至是JVM
- Exception,程序可能需捕获且处理的异常
Exception有个子类RuntimeException:程序虽无法继续执行,但还能抢救一下。
- RuntimeException和Error属于非检查异常(unchecked exception)
- 其它异常属于检查异常(checked exception)检查异常都要程序显式捕获或在方法声明中用throws关键字抛出。一般程序自定义的异常应都设计为检查异常,以便最大化利用Java编译器的编译时检查。
异常实例的构造十分昂贵
构造异常实例时,JVM需生成该异常的栈轨迹(stack trace)。该操作会逐一访问当前线程的Java栈帧,并记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名及在代码第几行触发异常。
生成栈轨迹时,JVM会忽略异常构造器及填充栈帧的Java方法(Throwable.fillInStackTrace),直接从新建异常位置开始算起。JVM还会忽略标记为不可见的Java方法栈帧。
是否可以缓存异常实例,在需要用时直接抛出?
语法上看,可以。然而,该异常对应栈轨迹并非throw语句的位置,而是新建异常的位置。因此,这可能误导开发人员定位到错误位置。这也是为何项目中都选择抛出新建的异常实例。
2 JVM如何捕获异常?
在编译生成的字节码中,每个方法都附带一个异常表。异常表中的每个条目代表一个异常处理器,且构成如下:
- from指针
- to指针
- target指针
- 所捕获的异常类型
这些指针的值是字节码索引(bytecode index,bci),用以定位字节码:
- from指针和to指针标示该异常处理器所监控的范围,如try代码块所覆盖的范围
- target指针指向异常处理器的起始位置,如catch代码块的起始位置
如以上main方法,catch代码块所捕获的异常类型为Exception。
编译后,该方法的异常表有个条目:
- from、to指针分别为0、3,代表监控范围为索引为0~3的字节码(不包括3)
- 该条目的target指针6,代表该异常处理器从索引为6的字节码开始
- 条目的最后一列,代表该异常处理器所捕获的异常类型是Exception
当程序异常,JVM会从上至下遍历异常表中所有条目。当触发异常的字节码的索引值在某异常表条目的监控范围内,JVM会判断所抛异常和该条目想捕获的异常是否匹配:
- 若匹配,JVM会将控制流转移至该条目target指针指向的字节码
若遍历完所有异常表条目,JVM仍未匹配到异常处理器,则会弹出当前方法对应的Java栈帧且在调用者重复上述操作。最坏情况下,JVM需遍历当前线程Java栈上所有方法的异常表。
finally代码块的编译:当前版本Java编译器的做法,复制finally代码块内容,分别放在try-catch代码块所有正常执行路径及异常执行路径出口。
异常执行路径,Java编译器会生成一或多个异常表条目,监控整个try-catch代码块,且捕获所有种类异常(在javap中以any指代)。这些异常表条目的target指针将指向另一份复制的finally代码块。且在该finally块最后,Java编译器会重新抛出所捕异常。
javap查看下面这段包含了try-catch-finally代码块编译结果:
编译结果包含三份finally代码块:
- 前两份分别位于try代码块和catch代码块的正常执行路径出口
- 最后一份作为异常处理器,监控try、catch代码块。捕获:try代码块触发、未被catch代码块捕获异常catch代码块触发的异常
若catch代码块捕获异常,并触发另一个异常,那finally捕获且重抛的异常是哪个?后者。即原本的异常会被忽略,这对代码调试很不利。
3 Supressed异常及语法糖
Java 7引入了Supressed异常来解决这个问题。这个新特性允许开发人员将一个异常附于另一个异常之上。因此,抛出的异常可以附带多个异常的信息。
然而,Java层面的finally代码块缺少指向所捕获异常的引用,所以这个新特性使用起来非常繁琐。
为此,Java 7专门构造了一个名为try-with-resources的语法糖,在字节码层面自动使用Supressed异常。当然,该语法糖的主要目的并不是使用Supressed异常,而是精简资源打开关闭的用法。
在Java 7之前,对于打开的资源,我们需要定义一个finally代码块,来确保该资源在正常或者异常执行状况下都能关闭。
资源的关闭操作易触发异常。若同时打开多个资源,则每个资源都对应一个独立try-finally代码块,以保证每个资源都能够关闭,代码变得繁琐。
Java 7的try-with-resources语法糖,极大简化上述代码。可在try关键字后声明并实例化实现了AutoCloseable接口的类,编译器将自动添加对应close()操作。在声明多个AutoCloseable实例时,编译生成的字节码类似上面手工编写代码的编译结果。相比手工代码,try-with-resources还会使用Supressed异常的功能,避免原异常“被消失”。
Java 7还支持在同一catch代码块捕获多种异常,实现很简单:生成多个异常表条目。
4 总结
Java异常分为Exception和Error两种,而Exception又分为RuntimeException和其他类型。RuntimeException和Error属于非检查异常。其他的Exception皆属于检查异常,在触发时需要显式捕获,或者在方法头用throws关键字声明。
Java字节码中,每个方法对应一个异常表。当程序触发异常时,Java虚拟机将查找异常表,并依此决定需要将控制流转移至哪个异常处理器之中。Java代码中的catch代码块和finally代码块都会生成异常表条目。
- 上一篇: Java异常之异常处理类详解和代码举例
- 下一篇: 小白也能看懂的Java异常处理机制
猜你喜欢
- 2024-11-20 Java中的异常
- 2024-11-20 小白也能看懂的Java异常处理机制
- 2024-11-20 Java异常之异常处理类详解和代码举例
- 2024-11-20 第25天|Java入门有野,异常处理
- 2024-11-20 java安全编码指南之:异常处理
- 2024-11-20 解读Java编程思想--异常处理
- 2024-11-20 Java中异常处理机制的详细解析及其优化示例代码
- 2024-11-20 学习java, 需要知道的异常处理
- 2024-11-20 java异常处理
- 2024-11-20 如何在Java Lambda表达式中处理异常和错误?
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)