1. JVM如何处理异常

  1. 在程序正常的情况下finally代码块会在try代码块后面运行,如果try代码块抛出了异常,又没有被相应的catch代码块捕获,那么finally代码块会执行完后重新抛出该异常;

  2. 如果异常被catch代码块捕获,则finally代码块在catch代码块之后运行;

  3. 如果catch代码块也抛出了异常,那么finally代码块会运行,然后抛出catch代码块中抛出的异常;

  4. 如果finally代码块也触发了异常,那么中止finally代码块的执行,往外抛出异常;

  5. 异常实例的构造十分昂贵,因为他会逐一访问当前线程的JAVA栈帧,并记录下各种调试信息,包括栈帧所指向的方法名,方法所在的类名、文件名以及在代码的第几行触发异常;

  6. 我们无法通过缓存异常实例,在需要的时候抛出来优化开销,缓存的实例记录的是创建该实例时候的栈信息,无法有效反应出错时候的栈信息,如果使用fillInStackTrace()函数重新更新栈帧信息(主要开销来源),那么仅仅就省了对象创建的开销,那又何必大费周章的缓存实例呢,况且缓存实例还会带来其他问题;

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

  8. from指针 和 to指针 分别记录了该异常处理器所监控的范围,如try代码块所覆盖的范围,target指针则指向了异常处理器的起始位置,如catch代码块的起始位置;

  9. 当程序出现异常时,Java虚拟机会从上至下遍历异常表中所有的条目,当触发异常的字节码索引值在某个异常处理器监控的范围之内时,Java虚拟机会判断所抛出的异常和该条目想要捕获的异常是否匹配,如果匹配,Java虚拟机会将控制流转移至该条目target指针所指向的字节码;

  10. 如果遍历完所有的条目,仍未找到匹配的异常处理器,则弹出当前方法对应的Java栈帧,并且在调用者中重复上述操作。在最坏的情况下,Java虚拟机需要遍历当前线程Java栈上所有的异常表;

  11. finally代码块的处理相对比较复杂, 目前Java编译器的做法是复制finally代码块的内容到try catch代码块的所有正常以及异常执行路径的出口中;

    11.1 try代码块不抛出异常的正常执行路径出口复制finally代码块的内容;

    11.2 try代码块抛出异常但是被catch代码块捕获并处理的执行出口,复制finally代码块的内容(个人觉得可以跟11.1中共用一份finally代码块,但实际上没有,还没有相通为什么) ;

    11.3 try代码块抛出异常未被catch代码块捕获,或者catch代码块中也抛出了异常,此时finally的处理比较麻烦,编译器生成一条或者多条异常处理器,监控整个try catch代码段的执行,并且捕获的异常对象为任意异常,target指向finally代码块的另一个拷贝,在执行完finally代码块后,重新抛出异常;

  12. 示例代码及编译结果(来自郑雨迪博士的深入理解Java虚拟机):

public class Foo {

private int tryBlock;

private int catchBlock;

private int finallyBlock;

private int methodExit;

public void test() {

try {

  tryBlock = 0;

} catch (Exception e) {

  catchBlock = 1;

} finally {

  finallyBlock = 2;

}

methodExit = 3;

}

}

$ javap -c Foo

public void test();

Code:

  0: aload_0

  1: iconst_0

  2: putfield      #20                // Field tryBlock:I

  5: goto          30

  8: astore_1

  9: aload_0

  10: iconst_1

  11: putfield      #22                // Field catchBlock:I

  14: aload_0

  15: iconst_2

  16: putfield      #24                // Field finallyBlock:I

  19: goto          35

  22: astore_2

  23: aload_0

  24: iconst_2

  25: putfield      #24                // Field finallyBlock:I

  28: aload_2

  29: athrow

  30: aload_0

  31: iconst_2

  32: putfield      #24                // Field finallyBlock:I

  35: aload_0

  36: iconst_3

  37: putfield      #26                // Field methodExit:I

  40: return

Exception table:

  from    to  target type

      0    5    8  Class java/lang/Exception

      0    14    22  any

  1. 如果catch代码块捕获了异常,并且又产生了异常,那么finally代码块捕获并重新抛出的异常将是catch代码块中产生的异常,原异常就丢失了;

  2. Java 7 引入了Supressed的异常来解决13中提到的问题,允许开发人员将一个异常附于另一个异常之上,但语法晦涩,不易使用,try-with-resources语法糖将会自动使用Supressed异常;

  3. 如果finally语句中存在return语句会如何?如果finally中存在return语句,首先,函数的返回值只可能是finally中return的值,不管try catch代码段中是否存在return语句(因为return的对象其实也是一个可以改变的变量,finally中改变了return的对象的值),其次,如果try-catch代码段中产生了未被捕获的异常,那么这个异常将丢失,因为虽然finally生成的异常处理器捕获了异常,但因为在重抛异常之前return了,所以重抛异常语句得不到执行;

开心一下:为什么很多女孩子都有体香,而男的则很少有?因为化妆品、护肤品已经腌入味了!(?纯属调侃)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值