前面一篇文章介绍了Java中异常的try-catch-final的原理(从字节码开始讲JVM是如何进行Java异常处理的(上)),今天我们就进一步分析一下JVM是如何实现它的。
JVM是如何抛异常的
要想了解try-catch-final的实现原理,那么就必须要先知道JVM是如何抛出异常信息的。以如下的代码为例来分析一下:
![cce7721cfe8bc1425312311e243d1440.png](https://img-blog.csdnimg.cn/img_convert/cce7721cfe8bc1425312311e243d1440.png)
这段代码通过javap命令得到如下信息
![2a1189fe5caa0cf6d44731d9cdb8e4a7.png](https://img-blog.csdnimg.cn/img_convert/2a1189fe5caa0cf6d44731d9cdb8e4a7.png)
代码很简单,对应的字节码也是非常的简单。看指令名称我们就可以很快的找到对应抛异常的指令:athrow指令。
athrow指令之前就是new 一个exception的代码,之后就是catch的代码了(从异常信息表里也能印证)。
知道了JVM是通过athrow指令来抛出异常的,接下来我们就看看JVM是如何实现athrow指令的。
athrow指令的实现原理
以JDK9为例,我们在JVM源码中全局搜索athrow,能够得到如下的信息:
![603144ccb8a4b52ba401e79aa915fe88.png](https://img-blog.csdnimg.cn/img_convert/603144ccb8a4b52ba401e79aa915fe88.png)
从文件名就知道,athrow在不同的平台具有不同的实现。其实他们的实现基本大同小异,最终都是会跳到Interpreter::throw_exception_entry()返回的抛异常入口代码。
throw_exception_entry()返回的是指令模板(什么是JVM的指令模板呢?以后有机会再介绍),实现如下:
![72d2c75bf22a7e66e0a1b14399296f82.png](https://img-blog.csdnimg.cn/img_convert/72d2c75bf22a7e66e0a1b14399296f82.png)
上面的代码主要实现的逻辑为:
![a82b17624120d2e1fe1ca6faa756bb31.png](https://img-blog.csdnimg.cn/img_convert/a82b17624120d2e1fe1ca6faa756bb31.png)
说明:
- 显然查找异常处理代码是整个过程最为重要的部分,它是通过call_VM指令完成的,我们等会进一步说明。
- 查找到的异常处理代码是通过jmp指令跳转的,所以处理完是不会回到原位的,这也就实现了异常后面的代码是不会被处理的。
至此,JVM抛异常的实现原理就基本介绍完了,接下来介绍JVM是如何实现对异常的catch的。
JVM是如何实现catch异常的
知道了JVM是如何实现throw的,那么catch的实现过程也就很好理解了。我们知道Java方法抛出的异常catch和finally语句块都会被记录在异常信息表中,并且它们两的实现上并没有本质的区别。上面也介绍了JVM在实现抛异常的时候,会先去查找异常的处理代码位置,JVM就是在这个异常信息表中查找的。
JVM查找异常处理handler的代码比较多就不贴全部代码了,主要实现过程如下图所示:
![9bf6a21c25a4a0dfdf874418154ab657.png](https://img-blog.csdnimg.cn/img_convert/9bf6a21c25a4a0dfdf874418154ab657.png)
其中最为关键的handler查找代码:
![ae5cdc2e381988882e40218b398a4834.png](https://img-blog.csdnimg.cn/img_convert/ae5cdc2e381988882e40218b398a4834.png)
这段代码会循环遍历异常信息表,然后找到当前执行位置在起始和结束位置之间的那一项,接下来判断这一项是否能够处理需要处理的exception。