Atitit.异常机制的设计原理

缺陷

关键是只要知晓有一个异常表的存在,try 的范围就是体现在异常表行记录的起点和终点。JVM 在 try 住的代码区间内如有异常抛出的话,就会在当前栈桢的异常表中,找到匹配类型的异常记录的入口指令号,然后跳到该指令处执行。异常指令块执行完后,再回来继 续执行后面的代码。JVM 按照每个入口在表中出现的顺序进行检索,如果没有发现匹配的项,JVM 将当前栈帧从栈中弹出,再次抛出同样的异常。当 JVM 弹出当前栈帧时,JVM 马上终止当前方法的执行,并且返回到调用本方法的方法中,但是并非继续正常执行该方法,而是在该方法中抛出同样的异常,这就使得 JVM 在该方法中再次执行同样的搜寻异常表的操作。
作者:: 绰号:老哇的爪子 ( 全名::Attilax Akbar Al Rapanui 阿提拉克斯 阿克巴 阿尔 拉帕努伊 ) 汉字名:艾龙,  EMAIL:1466519819@qq.com

转载请注明来源: http://blog.csdn.net/attilax

上面那样的内层方法无法处理异常的层层向外抛,层层压栈,这样就形成一个异常栈。异常栈十分有利于我们透析问题之所在,例如 e.printStackTrace(); 或者带参数的 e.printStackTrace(); 方法可将异常栈信息定向输出到他处,还有 log4j 的 log.error(Throwable) 也有此功效。若是在行径的哪层有能力处理该异常则已,否则直至 JVM,直接造成 JVM 崩溃掉。例如当 main() 方法也把异常抛了出去,JVM 此刻也就到了生命的尽头。

解释一下这段字节码的运作过程,如果平时看字节码比较多的同学可以直接略过这段。偏移地址为0的new指令首先会在常量池找到第58项常量,此常量现在为CONSTANT_Class_info型的符号引用,类解析阶段被翻译为java.lang.Exception类的直接引用,接着虚拟机会在Java堆中开辟相应大小的实例空间,并将此空间的引用压入操作栈的栈顶。偏移为3的dup指令就简单的把栈顶的值复制了一份,重新压入栈顶,这时候操作栈中有2份刚刚new出来的exception对象的引用。偏移为4的invokespecial指令将第一个exception对象引用出栈,以它为接收者调用了Excepiton类的实例构造器,这句执行完后栈顶还剩下一份exception对象的引用。写了那么多,说白了这3条字节码就是干了“new Exception()”这句Java代码应该做的事情,和创建任何一个Java对象没有任何区别。这一部分耗费的时间在上一节中分析过,创建一个异常对象只占创建、抛出并接住异常的20%时间。 
        接着是占用80%时间高潮部分,偏移为7的athrow指令,这个指令运作过程大致是首先检查操作栈顶,这时栈顶必须存在一个reference类型的值,并且是java.lang.Throwable的子类(虚拟机规范中要求如果遇到null则当作NPE异常使用),然后暂时先把这个引用出栈,接着搜索本方法的异常表(异常表是什么等写完这段再说),找一下本方法中是否有能处理这个异常的handler,如果能找到合适的handler就会重新初始化PC寄存器指针指向此异常handler的第一个指令的偏移地址。接着把当前栈帧的操作栈清空,再把刚刚出栈的引用重新入栈。如果在当前方法中很悲剧的找不到handler,那只好把当前方法的栈帧出栈(这个栈是VM栈,不要和前面的操作栈搞混了,栈帧出栈就意味着当前方法退出),这个方法的调用者的栈帧就自然在这条线程VM栈的栈顶了,然后再对这个新的当前方法再做一次刚才做过的异常handler搜索,如果还是找不到,继续把这个栈帧踢掉,这样一直到找,要么找到一个能使用的handler,转到这个handler的第一条指令开始继续执行,要么把VM栈的栈帧抛光了都没有找到期望的handler,这样的话这条线程就只好被迫终止、退出了。

Java代码  

1. Exception table:

2.  from   to  target type

3.    0     8     8   Class java/lang/Exception

上面的异常表只有一个handler记录,它指明了从偏移地址0开始(包含0),到偏移地址8结束(不包含8),如果出现了java.lang.Exception类型的异常,那么就把PC寄存器指针转到8开始继续执行。顺便说一下,对于Java语言中的关键字catch和finally,虚拟机中并没有特殊的字节码指令去支持它们,都是通过编译器生成字节码片段以及不同的异常处理器来实现

字节码指令还剩下2句,把它们说完。偏移地址为8的astore_1指令,作用是把栈顶的值放到第一个Slot的局部变量表中,刚才说过如果出现异常后,虚拟机找到了handler,会把那个出栈的异常引用重新入栈。因此这句astore_1实现的目的就是让catch块中的代码能访问到“catch (Exception e)”所定义的那个“e”,又顺便提一句,局部变量表从0开始,第0个Slot放的是方法接收者的引用,也就是使用this关键能访问的那个对象。最后的return指令就不必多讲了,是void方法的返回指令,因为我们的catch块里面没有内容,所以立刻就return了。 
    到此为止,这几句字节码讲完了,我们总结一下athrow指令中虚拟机可能做的事情(只会做其中一部份啦):

· 检查栈顶异常对象类型(只检查是不是null,是否referance类型,是否Throwable的子类一般在类验证阶段的数据流分析中做,或者索性不做靠编译器保证了,编译时写到Code属性的StackMapTable中,在加载时仅做类型验证)

· 把异常对象的引用出栈

· 搜索异常表,找到匹配的异常handler

· 重置PC寄存器状态

· 清理操作栈

· 把异常对象的引用入栈

· 把异常方法的栈帧逐个出栈(这里的栈是VM栈)

· 残忍地终止掉当前线程。

· ……

第一句提取操作栈中引用的异常对象,第二句检查异常是否为空,虚拟机规范中要求的为null就当NPE异常,就是这句实现的:

注释中说可以使用“common code”是指handle_return中的代码,每条opcode处理完都会转到这段代码。因为异常不一定来自athrow指令,也就是不一定来自于用户程序直接抛出,虚拟机运作期间也会产生异常,如被0除、空指针,严重一点的OOM神马的。所以出现异常后的方法退出动作在通用的handle_return里面根据pending_exception进行处理,代码太多就不贴了。前面几句没有太特别的动作,看来athrow指令的关键实现还是在handle_exception这节,看看它的代码(为了逻辑清晰,我删除了不必要的代码,譬如支持跟踪调试的语句)

看这段代码的关键部分,CALL_VM那句是查找异常表,所执行的InterpreterRuntime::exception_handler_for_exception在同目录下的interpreterRuntime.cpp中,查找的具体过程有点复杂,只看程序的主体脉络,这里的代码就不再牵扯进来了。如果找到,也就是if (continuation_bci >= 0)成立的话,(bci的意思是Bytecode Index,字节码索引),把异常对象重新入栈(SET_STACK_OBJECT(except_oop(), 0)这句),并且重置PC指针为异常handler的起始位置(pc = METHOD->code_base() + continuation_bci这句),然后跳转到run处开始下一轮的循环switch过程。查询异常表没有找到合适的handler,那重新设置上pending_exception,因为前面的时候使用clear_pending_exception()清除掉了。在handle_return中会根据这个标志来决定方法是否出现异常,要不要退出。虚拟机规范中要求的athrow指令的动作这里就写完了,HotSpot VM我们写不出来,看一下还是可以的嘛。

透过JVM看Exception本质 - Java综合 - Java - ITeye论坛.htm

JVM 对Java异常的处理原理 - 旭灿的专栏 - 博客频道 - CSDN.NET.htm

Atitit.异常机制的设计原理相关推荐

  1. 《深入理解mybatis原理》 MyBatis缓存机制的设计与实现

    本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论. MyBatis将数据缓存设计成两级结构,分为一级缓存 ...

  2. Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结

    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结 1. 微内核与插件的优点1 2. 插件的注册与使用2 2.1. Ioc容器中注册插件2 2.2. 启动器微内核启动3 ...

  3. Kafka中副本机制的设计和原理

    Kafka中一个分区可以拥有多个副本,副本可分布于多台机器上.而在多个副本中,只会有一个Leader副本与客户端交互,也就是读写数据.其他则作为Follower副本,负责同步Leader的数据,当Le ...

  4. Atitit.木马病毒websql的原理跟个设计

    Atitit.木马病毒websql的原理跟个设计 1. Keyword Wsql { var sql="select "+p.txt+" as t,"+p.v+ ...

  5. kafka入门:简介、使用场景、设计原理、主要配置及集群搭建

    为什么80%的码农都做不了架构师?>>>    kafka入门:简介.使用场景.设计原理.主要配置及集群搭建(转) 问题导读: 1.zookeeper在kafka的作用是什么? 2. ...

  6. kafka入门:简介、使用场景、设计原理、主要配置及集群搭建--转载

    问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行"随机读写"的原因是什么? 3.kafka集群consumer和producer状 ...

  7. kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)

    问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行"随机读写"的原因是什么? 3.kafka集群consumer和producer状 ...

  8. kafka原理_P8架构师带你参透Kafka:设计原理、消息存储、消息消费原理等等

    本文转载自: linkedkeeper.com,作者:张松然 推荐阅读: 一个月面试了3家大厂Java岗,我发现这几个突破点 目录 Kafka的基本介绍 Kafka的设计原理分析 Kafka数据传输的 ...

  9. Atitit.事件机制 与 消息机制的联系与区别

    Atitit.事件机制 与 消息机制的联系与区别 1. 消息/事件机制是几乎所有开发语言都有的机制,在某些语言称之为消息(Event),有些地方称之为(Message).1 2. 发布/订阅模式1 3 ...

  10. JVM 垃圾回收算法机制及其实现原理

    前言 对于 JVM 来说,我们都不陌生,其是 Java Virtual Machine(Java 虚拟机)的缩写,它也是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.JV ...

最新文章

  1. 【青少年编程】【二级】货运飞船
  2. asp.net中防止攻击的方法
  3. 解决 Angular 官网下载的库 Schematics 在 windows 环境不支持 .. 的临时解决方案
  4. spring定时任务的配置使用
  5. 美科技股崩了,Facebook、亚马逊、谷歌一夜蒸发万亿元
  6. 问题-Delphi编译时提示缺少delphi自己的单元文件
  7. asp.net mvc使用mysql_如何使用MySQL设置ASP.NET MVC 2?
  8. x86汇编之任务切换
  9. 在线预览word,excel,ppt
  10. Live2D和wallpaperEngine公用
  11. SDM连接小凡的模拟路由器并配置图文并貌详细解说
  12. 3.UEFI-edk2 增加中文显示
  13. 永洪BI开发脚本总结
  14. vmware卸载不干净,重装不了问题;装了虚拟机上不了网问题;装了虚拟机发现windows在cmd中ping不了虚拟机,虚拟机能ping的了windows;secureCRT或者filezilla连接
  15. ac1900修改代理服务器,tplink ac1900路由器怎么设置?
  16. html实现用户调查的表单网页,江苏开放大学网页制作基础及HTML测试作业二制作1个E游调查的表单网页...
  17. 计算机硬盘没显示,移动硬盘不显示在我的电脑
  18. android德地图点聚合,点聚合-点标记-示例中心-JS API 示例 | 高德地图API
  19. 麒麟系统(Fedora 64)安装谷歌(chrome)浏览器
  20. 基于Pytorch的上市公司财务预警分析

热门文章

  1. C++异常处理之terminate函数和set_terminate()函数
  2. [python]上传文件验证
  3. 巴西发生矿坝决堤事故 外媒:90名受困者已救出
  4. 利用border设置transparent绘制图形
  5. Node.js .sh scripts Manager
  6. 【转】android:windowSoftInputMode属性详解
  7. VI/VIM常用命令
  8. 为什么我们做不到【领导篇】作者:金刚波
  9. 第14章 系统异常情况记录
  10. MySQL-第十二篇管理结果集