java虚拟机是如何处理异常的

异常的基本概念

Throwable

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

Exception

Exception涵盖程序可能需要捕获的异常,除了RuntimeException以外,其他类型的异常都需要显示的捕获或throws;

Error

当触发Error时,表示执行状态已无法恢复,需要终止线程或终止虚拟机;

RuntimeException&Error

这两个异常都属于非检查型异常,其他的异常都属于检查型异常,Java中所有的检查型异常都需要显示的捕获或者throws;

 

构造异常的成本

构造异常的成本十分昂贵,构造异常时,需要生成该异常的栈轨迹,因此虚拟机需要注意扫描当前线程的栈帧,并记录下方法名、类名、文件名以及第几行代码触发的异常等信息;

构造异常时直接从创建异常实例的位置开始算起;

捕获异常

捕获异常设计三个代码块,try、catch、finally;

try

用来标记需要监控异常的代码;

catch

用来捕获try代码块中触发的某种指定异常,try代码块后面可以跟多个catch用来捕获不同类型的异常,Java虚拟机会从上至下的匹配异常处理器;

finally

跟在try和catch代码块之后,用来声明一段必定运行的代码;

finally代码块执行时机

在程序正常执行的情况下,finally代码会在 try 代码块之后运行。否则,也就是 try 代码块触发异常的情况下,如果该异常没有被捕获,finally 代码块会直接运行,并且在运行之后重新抛出该异常。如果该异常被 catch 代码块捕获,finally 代码块则在 catch 代码块之后运行。在某些不幸的情况下,catch 代码块也触发了异常,那么 finally 代码块同样会运行,并会抛出 catch 代码块触发的异常。在某些极端不幸的情况下,finally 代码块也触发了异常,那么只好中断当前 finally 代码块的执行,并往外抛异常。

如果catch捕获了异常并且catch中的代码触发了另外一个异常,则finally只会抛出最后一个触发的异常;

虚拟机是如何捕获异常的

编译生成的字节码中,每个方法都有一个对应的异常表,表中的每一个条目代表一个异常处理器;异常表的条目由from指针、to指针、target指针以及所捕获的异常类型构成;

from指针 & to指针 & target指针

from和to表示该异常所要监控的范围,比如try代码块,target指针表示异常处理的起始位置,比如说catch代码块的起始位置;

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&to范围内进行查找,并判断异常是否和type类型匹配,如果匹配则执行target指针执行的字节码,如果遍历了异常表中所有的条目仍未匹配到处理器,则会弹出当前方法的栈帧并在调用者上重复上述操作;

finally代码块的编译

会把finally代码块的语句复制到每一个控制流的代码后面;

 

一个catch中捕获多个异常

虚拟机的实现方式是为多个异常生成了多个异常表条目;

// 在同一catch代码块中捕获多种异常
try {
  ...
} catch (SomeException | OtherException e) {
  ...
}

语法糖:Suppressed & try-witch-resource

Suppressed

允许把一个异常附于另一个异常之上,从而抛出的异常可以附带多个异常;

// 效果:
Exception in thread "main" java.lang.RuntimeException: Initial
        at Foo.main(Foo.java:18)
        Suppressed: java.lang.RuntimeException: Foo2
                at Foo.close(Foo.java:13)
                at Foo.main(Foo.java:19)
        Suppressed: java.lang.RuntimeException: Foo1
                at Foo.close(Foo.java:13)
                at Foo.main(Foo.java:19)
        Suppressed: java.lang.RuntimeException: Foo0
                at Foo.close(Foo.java:13)
                at Foo.main(Foo.java:19)

try-witch-resource

这个语法糖主要用来解决关闭多个资源的情况,try中允许声明实现了AutoCloseable接口的实例化类,编译器会自动添加对应的close;

该语法糖会自动使用Suppressed来避免多个异常被覆盖;

public class Foo implements AutoCloseable {
  private final String name;
  public Foo(String name) { this.name = name; }

  @Override
  public void close() {
    throw new RuntimeException(name);
  }

  public static void main(String[] args) {
    // try-with-resources
    try (Foo foo0 = new Foo("Foo0");
         Foo foo1 = new Foo("Foo1");
         Foo foo2 = new Foo("Foo2")) {
      throw new RuntimeException("Initial");
    }
  }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值