【05】JVM是如何处理异常的

一、异常的概念

所有异常都是Throwable类或者其子类的实例。

Throwable有两大直接子类,第一个是ERROR,涵盖程序不应捕获的异常,当程序触发Error时,其执行状态已经无法恢复,需要中止线程甚至是终止虚拟机;第二个是Exception,涵盖程序可能需要捕获并且处理的异常。

捕获异常涉及三种代码块

(1)try代码块:用来标记需要进行异常监控的代码

(2)catch代码块:跟在try代码块之后,用来捕获在try代码块中触发的某种特定类型的异常。Java虚拟机会从上而下匹配异常处理器,因此,前面的catch代码块所捕获的异常类型不能覆盖后边的,否则编译器会报错

(3)finally代码块:跟在try代码块和catch代码块之后,用来声明一段必定运行的代码。它的设计初衷是为了避免跳过某些关键的清理代码,例如关闭已经打开的系统资源。

二、Java虚拟机是如何捕获异常的

在编译生成的字节码中,每个方法都附带一个异常表。异常表中的每个条目代表一个异常处理器,由from指针、to指针、target指针及其所捕获的异常类型构成。这些指针的值是字节码索引,用以定位字节码。

java 复制代码
public static void main(String[] args) {
  try {
    mayThrowException();
  } catch (Exception e) {
    e.printStackTrace();
  }
}
// 对应的 Java 字节码
public static void main(java.lang.String[]);
  Code:
    0: invokestatic mayThrowException:()V
    3: goto 11
    6: astore_1
    7: aload_1
    8: invokevirtual java.lang.Exception.printStackTrace
   11: return
// 编译后,该方法异常表拥有如下一个条目
  Exception table:
    from  to target type
      0   3   6  Class java/lang/Exception  // 异常表条目
      
// from 为0 to 为 3代表它的监控范围从索引为0的字节码开始,到索引为3的字节码结束(不包括3)
// target为6代表该索引处理器从索引为6的字节码开始。
//type 代表异常处理器捕获的异常类型是Exception

当程序触发异常时,Java虚拟机会遍历异常表中的所有条目。当触发异常的字节码的索引值在异常条目的监控范围内,JVM会判断所抛出的异常和该条目想要捕获的异常是否匹配。若匹配,JVM会将控制流转移至该条目target指针指向的字节码。

若遍历完所有异常表条目,JVM仍未匹配到异常处理器,则会弹出对应的Java栈帧,并且在调用者中重复上述操作。

思考:

1:使用异常捕获的代码为什么比较耗费性能?

因为构造异常的实例比较耗性能。这从代码层面很难理解,不过站在JVM的角度来看就简单了,因为JVM在构造异常实例时需要生成该异常的栈轨迹。这个操作会逐一访问当前线程的栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常等信息。

虽然具体不清楚JVM的实现细节,但是看描述这件事情也是比较费时费力的。

2:finally是怎么实现无论异常与否都能被执行的?

这个事情是由编译器来实现的,现在的做法是这样的,编译器在编译Java代码时,会复制finally代码块的内容,然后分别放在try-catch代码块所有的正常执行路径及异常执行路径的出口中。

相关推荐
东阳马生架构17 小时前
JVM简介—3.JVM的执行子系统
jvm
程序员志哥1 天前
JVM系列(十三) -常用调优工具介绍
jvm
后台技术汇1 天前
JavaAgent技术应用和原理:JVM持久化监控
jvm
程序员志哥1 天前
JVM系列(十二) -常用调优命令汇总
jvm
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭1 天前
聊聊volatile的实现原理?
java·jvm·redis
_LiuYan_1 天前
JVM执行引擎JIT深度剖析
java·jvm
王佑辉1 天前
【jvm】内存泄漏的8种情况
jvm
工业甲酰苯胺1 天前
JVM简介—1.Java内存区域
java·jvm·python
yuanbenshidiaos2 天前
c++---------数据类型
java·jvm·c++