通过字节码理解try-catch-finally
通过字节码理解try-catch-finally
场景
对于以下代码:
public int test() { int x; try { x = 1; return x; } catch (Exception e) { x = 2; return x; } finally { x = 3; } }
只听到从山间传来架构君的声音:
路回临石岸,树老出墙根。
有谁来对上联或下联?
结论
- 如果try语句没有出现属于Exception或其子类的异常,返回值为1
- 如果出现,返回值为2
- 如果出现Exception以外的其它异常,则没有返回,方法异常退出
解释
通过javap获取的字节码如下(主体部分):
此代码由Java架构师必看网-架构君整理
public int test(); descriptor: ()I flags: ACC_PUBLIC Code: stack=1, locals=5, args_size=1 0: iconst_1 //将int类型常量1压入操作数栈-> 操作数栈:1,局部变量表:空 1: istore_1 //将int类型值出栈,存入局部变量1-> 操作数栈:空,局部变量表:slot1=1,也就是x=1 2: iload_1 //从局部变量表1中装载int类型值,压入操作数栈-> 操作数栈:1,局部变量表:slot1=1 3: istore_2 //将int类型值出栈,存入局部变量2-> 操作数栈:空,局部变量表:slot1=1,slot2=1 4: iconst_3 //将int类型常量3压入操作数栈-> 操作数栈:3,局部变量表:slot1=1,slot2=1 5: istore_1 //将int类型值出栈,存入局部变量1-> 操作数栈:空,局部变量表:slot1=3,slot2=1 6: iload_2 //从局部变量表2中装载int类型值,压入操作数栈-> 操作数栈:1,局部变量表:slot1=3,slot2=1 7: ireturn //返回int类型,此时操作数栈顶为1,所以返回1(无异常情况) 8: astore_2 //当0到3行出现异常跳至这里,将catch中的Exception e复制,存入局部变量表2->slot2 = e 9: iconst_2 //将int类型常量2压入操作数栈-> 操作数栈:2,局部变量表:slot2=e 10: istore_1 //将int类型值出栈,存入局部变量表1-> 操作数栈:空,局部变量表:slot1=2 11: iload_1 //从局部变量表1中装载int类型值,压入操作数栈-> 操作数栈:2,局部变量表:slot1=2 12: istore_3 //将int类型值出栈,存入局部变量表3-> 操作数栈:空,局部变量表:slot1=2,slot3=2 13: iconst_3 //将int类型常量3压入操作数栈-> 操作数栈:3,局部变量表:slot1=2,slot3=2 14: istore_1 //将int类型值出栈,存入局部变量表1-> 操作数栈:空,局部变量表:slot1=3,slot3=2 15: iload_3 //从局部变量表3中装载int类型值,压入操作数栈-> 操作数栈:2,局部变量表:slot1=3,slot3=2 16: ireturn //返回int类型,此时操作数栈顶为2,所以返回2(Exception 情况) 17: astore 4 //不属于Exception及其子类的异常存入局部变量表4-> 局部变量表:slot4=异常引用 19: iconst_3 //将int类型常量3压入操作数栈-> 操作数栈:3,局部变量表:slot4=异常引用 20: istore_1 //将int类型值出栈,存入局部变量表1-> 操作数栈:空,局部变量表:slot1=3,slot4=异常引用 21: aload 4 //将局部变量表4中的异常引用压入栈顶 23: athrow //抛出栈顶异常 Exception table: from to target type 0 4 8 Class java/lang/Exception 0 4 17 any 8 13 17 any 17 19 17 any
上述Code中每行字节码都添加了注释,分析了操作数栈和局部变量表的状态。
其中,字节码行号0到7就是没有异常时的字节码流,返回值为1。
需要注意的是,上述Code中,第4、5行即为finally中的:x=3。编译器自动在每段可能的分支路径之后都将finally语句块的内容冗余生成一遍来实现finally语义。同样的还有第13、14行,第19、20行。
然后我们来看异常处理表:
Exception table: from to target type 0 4 8 Class java/lang/Exception 0 4 17 any 8 13 17 any 17 19 17 any
其表示的意思是,在字节码行号中:
- 0到3行如果发现Exception或其子类异常,则跳到第8行处理;如果发现其它异常,则跳到第17行处理
- 8到12行,则跳到17行处理
- 异常表中的第四行:17 19 17 any (这里还不理解什么意思,翻阅了虚拟机规范关于异常表的说明,只说了to(也就是end_pc,其排他性是设计上的历史错误,这里from和target相同的问题等查到相关资料再作补充,可能是虚拟机为了应对某种情况特殊处理的,不行还是得翻阅源码~)
结合这个异常表和Code中的注释,可以发现,如果try语句中发生了Exception及其子类异常,那么执行的字节码为第8-16行,最终返回值为2。其他异常的话,则跳到第17行处理,执行第17-23行,最终将异常抛出,方法值没有返回。
从异常表中还可以发现另一问题,在catch块中如果出现了异常(第8到12行),那么也会跳到第17行进行处理,也就是执行finally代码块。
注:对于x=1;这条语句,虽然在字节码中体现为iconst和istore两条字节码,但是他们依然是原子操作,原子操作不是说只有一条指令,而是不可中断的一个或一系列操作。可以对比一下long和double的非原子协议。
参考:《深入理解Java虚拟机》
相关文章
- 天秀!花费 200W 设计的新版 “小米”图标,看看用Python怎么绘制?
- 通过漫天花雨来入门 Three.js
- 用 Three.js 画个 3D 生日蛋糕送给他(她)
- 分享一道Excel面试题:文本和数值的 “自定义” 排序!
- 玩儿 “爬虫” 朋友最爱,都2021了,这个Python库你还不知道吗?
- 一级棒!这应该是最好的 “re正则表达式” 使用教程了!
- 【阅读3.8w,收藏2000次】97页《Python自动化办公文档》,让您的 “办公效率” 提高100倍,拿走即用!
- 别整天 “学妹/前女友”了,花2小时整理了Numpy测试习题100道,做个测验吧!
- 总结了12个Numpy高级函数,完美解决数据处理,拿来即用!
- 知识盲区 | 那些年,你不太会用的Excel “摩斯密码”!
- TensorFlow官方出了个GNN框架,YYDS!
- 史上最全的Numpy函数大全,导师都说我总结的好!
- Linux网络管理,NAT网络配置,修改主机名称,主机映射,防火墙,系统启动级别,用户和用户组,为用户配置sudoer权限,文件权限管理,打包和压缩
- MySQL8.0 角色管理
- MySQL8.0---Create user的那些属性
- MyISAM表坏了咋整?myisamchk试一手
- Spring Security入门案例
- springboot日志系统的设计缺陷
- SpringBoot中的缓存穿透、击穿、雪崩解决方案
- 常见算法题