Try-catch-finally在JVM底层都干了些啥?

  让我们准备一个函数:

  


  然后,反编译他的字节码:

  


  首先我们介绍异常表:在编译生成的字节码中,每个方法都附带一个异常表。

  异常表中的每一个条目代表一个异常处理器,并且由 from 指针、to 指针、target 指针以及所捕获的异常类型构成。这些指针的值是字节码索引用以定位字节码。

  下图就是我特别指出的JVM字节码中的异常表部分。

  


  我们来分析一下这几个语句的执行流程,首先执行:

  


  这相当于执行:

  


  再来:

  


  上图相当于执行

  


  有人问:try去哪了?

  我马上就要介绍。此时idiv执行完就有异常了,有异常了先找异常表。

  


  我再贴一下异常表,他是怎么搜索的呢?

  当程序触发异常时,Java 虚拟机会从上至下遍历异常表中的所有条目。

  当触发异常的字节码的索引值在某个异常表条目的监控范围内,Java 虚拟机会判断所抛出的异常和该条目想要捕获的异常是否匹配。

  如果匹配,Java 虚拟机会将控制流转移至该条目 target 指针指向的字节码。

  我们看 ,是第四个索引指向的字节码出了问题,显然,此时应该匹配红线这一条记录,从而跳转到第14个索引的字节码。

  


  我们看他怎么做的?

  


  new出一个RuntimeException并抛出,它就是

  


  这一句,按照我们刚才的流程,此时依然需要找到这个RuntimeException在哪个异常表的条目中

  


  此时匹配到异常表的条目,跳转到字节码索引23

  


  继续抛出RuntimeExcpetion,可以注意到 这实际上对应了

  


  这个语句,于是我们可以知道,在三个都出现异常的情况下,实际上最终向外抛出的异常是finally里面的异常。

  可以看到当31索引处调用athrow语句抛出异常时,此时异常表没有任何一个条目能够匹配该异常,此时怎么办呢?

  


  如果遍历完所有异常表条目,Java 虚拟机仍未匹配到异常处理器,那么它会弹出当前方法对应的 Java 栈帧,并且在调用者中重复上述操作。

  在最坏情况下,Java 虚拟机需要遍历当前线程 Java 栈上所有方法的异常表

  事实上分析以上的整体的全部语句你可以发现,jvm层面有真正的finally吗?

  没有

  现在的做法是,复制 finally 代码块的内容,分别放在 try-catch 代码块所有正常执行路径以及异常执行路径的出口中。无论是否出现异常,确保一定会执行finally语句。

  刚才catch出了异常,依然执行finally语句就可以发现这一点。至于其他路径,大家可以自行验证。我就在这里抛砖引玉了。

  至于为什么2-6发生任何异常都跳转到23?大家可以自己想一下这个问题。

  我就提示一点 2-6 target为14的条目代表的catch是不能捕获所有异常的,但是你要确保finally的语句能够执行。而2-6恰巧是try语句块的内容。23这个索引恰巧是finally语句的一份复制。


转载于:https://juejin.im/post/5ceb95766fb9a07ea33bf43b

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值