异常表
每一个 try语句块 catch的异常都与异常表中的一项相对应,异常表中的每一项都包括:
-
起点
-
终点,始终把 catch异常位置的 pc指针偏移量的最大值大1
-
处理异常时跳转到的字节码序列中的 pc指针偏移量
-
被 catch的异常类的常量池索引
例如:




















用 javap –c 查看字节码如下:

























可见 ClassNotFoundException异常可能会在 0~6之间抛出, 9开始处的代码处理此异常。
当产生异常的时候, jvm将会在整个异常表中搜索与之匹配的项,如果当前 pc在异常表入口所指的范围内,并且所抛出的异常是此入口所指向的类或者其子类,则跳转到对应的处理代码继续执行。
方法可能会抛出哪些已检查异常
Class文件的 attribute_info中保存有 Exceptions属性,记录着每个方法 throws的异常信息。具体的可以查看 class类文件格式相关的文章。
athrow指令从栈顶弹出 Throwable对象引用,抛出异常。
finally语句
在 jvm规范中, finally语句是通过 jsr/jsr_w与 ret指令实现的。当执行 jsr/jsr_w的时候将 finally执行完成后的返回地址压入栈中,进入 finally后会马上将此地址保存到一个局部变量中,执行完成后, ret从此局部变量中取出返回地址。???为什么会先把返回地址保存到局部变量中呢???因为,当从 finally语句返回的时候需要将返回地址成栈中弹出,当 finally语句非正常结束 (break,continue,return, 抛异常 )的时候就不用再考虑这个问题。
以下是 jvm 规范中 Compiling finally 的一段:
































当 tryItOut排除任何异常后都将会被异常表中的 any项捕获,执行完 finally后,会执行 athrow指令将异常抛出。
从 jdk的某一个版本开始就不会编译出编译出含 jsr/jsr_w、 ret的字节码了,因为有指令上的缺陷,导致 jvm的检验和分析系统出现漏洞。
再说 finally的非正常退出
在 finally中使用 break、 continue、 return、抛出异常等认为是 finally的非正常结束。非正常结束的时候, ret指令不会被执行,很可能会出现意想不到的结果。如:





































建议:在写
finally
语句的时候,尽量避免非正常结束!