深入拆解 Java 虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
86502 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
模块四:黑科技 (3讲)
深入拆解 Java 虚拟机
15
15
1.0x
00:00/00:00
登录|注册

06 | JVM是如何处理异常的?

你好,我是郑雨迪。今天我们来讲讲 Java 虚拟机的异常处理。
众所周知,异常处理的两大组成要素是抛出异常和捕获异常。这两大要素共同实现程序控制流的非正常转移。
抛出异常可分为显式和隐式两种。显式抛异常的主体是应用程序,它指的是在程序中使用“throw”关键字,手动将异常实例抛出。隐式抛异常的主体则是 Java 虚拟机,它指的是 Java 虚拟机在执行过程中,碰到无法继续执行的异常状态,自动抛出异常。举例来说,Java 虚拟机在执行读取数组操作时,发现输入的索引值是负数,故而抛出数组索引越界异常(ArrayIndexOutOfBoundsException)。
捕获异常则涉及了如下三种代码块。
try 代码块:用来标记需要进行异常监控的代码。
catch 代码块:跟在 try 代码块之后,用来捕获在 try 代码块中触发的某种指定类型的异常。除了声明所捕获异常的类型之外,catch 代码块还定义了针对该异常类型的异常处理器。在 Java 中,try 代码块后面可以跟着多个 catch 代码块,来捕获不同类型的异常。Java 虚拟机会从上至下匹配异常处理器。因此,前面的 catch 代码块所捕获的异常类型不能覆盖后边的,否则编译器会报错。
finally 代码块:跟在 try 代码块和 catch 代码块之后,用来声明一段必定运行的代码。它的设计初衷是为了避免跳过某些关键的清理代码,例如关闭已打开的系统资源。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Java 虚拟机》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(54)

  • 最新
  • 精选
  • 阿坤
    如果finally有return语句,catch内throw的异常会被忽略,这个从jvm层面怎么解释呢?

    作者回复: catch里抛的异常会被finally捕获了,再执行完finally代码后重新抛出该异常。由于finally代码块有个return语句,在重新抛出前就返回了。 你可以利用这篇文章的知识,就着javap的输出,分析一下具体的程序路径

    61
  • 曲东方
    throw exception性能差fillstacktrace除了遍历堆栈以外,如果有inline 代码消除等编译优化发生,是不是要先“去优化”完了再fill?要不然可能出现错误堆栈和代码对不上的情况 throw exception估计也会影响jit的优化,进而影响整体性能

    作者回复: 思考得很深,赞一个! 即时编译器生成的代码会保存原始的栈信息,以便去优化时能够复原。fillStackTrace也会读取这些信息的,所以不用先去优化再fill。 抛异常本身带来了额外的执行路径。通常如果能够将异常处理器也编译进去,那么不会有太大影响。

    2
    41
  • 李二木
    看完今天的文章有几个疑问 1方法的异常表是包含RuntimeException这种非check类型的异常吧?如果是那么每个方法都有异常表,那么是不是每个异常表中都有像ArrayIndexOutOfBoundsException这类型异常了。这类公共异常是私有还是共享呢 2像catch自定义异常,也会添加的当前方法的异常表里吗? 3 我们常常看到的异常调用栈,这里方法调用信息其实就是弹出方法栈帧吗?

    作者回复: 1 检查异常这个概念只在源代码中出现。异常表不是声明这段代码所有有可能抛出的异常,而是声明会被捕获的异常。 2 会的 3 栈轨迹 跟 弹出方法栈帧 是两个概念。你可以直接新建一个异常,然后不抛出,直接打印调用栈。这个时候是不会弹出当前栈帧的。

    2
    14
  • 吴伟
    检查异常和非检查异常也就是其他书籍中说的编译期异常和运行时异常?

    作者回复: 编译期异常和运行时异常这种划分有点奇怪。 检查异常也会在运行过程中抛出。但是它会要求编译器检查代码有没有显式地处理该异常。非检查异常包括Error和RuntimeException(会不会那本书直译为”运行时异常”?),这两个则不要求编译器显式处理。

    11
  • 李双迎
    老师,如果异常构造比较耗时,那么能否通过缓存同一位置相同异常的实例,来解决呢?

    作者回复: 理论上是可以的,一般不这么做,原因有两个,一是异常路径无需考虑性能,二是代码可读性。

    10
  • 子清
    如果在业务层的代码中使用Assert来判断参数是否有问题,然后在调用方捕捉异常,这样会不会耗性能

    作者回复: 首先走抛出异常捕获异常的异常执行路径的话,性能肯定是很慢的,因此最好在参数出现问题的概率很小的情况下使用这种方式。 另外,你说的Assert是某个库的工具类,还是assert语句?后者的话,一般只在开发环境中启用吧。

    5
  • Ennis LM
    Java 虚拟机会忽略掉异常构造器以及填充栈帧的 Java 方法(Throwable.fillInStackTrace),直接从新建异常位置开始算起。 Java 虚拟机还会忽略标记为不可见的 Java 方法栈帧。 请问老师,填充栈帧的 Java 方法和不可见的 Java 方法栈帧,是什么

    作者回复: 前者指Throwable.fillStackTrace以及异常的构造器,后者为Java虚拟机不想让用户看到的栈帧,比如说方法句柄的适配器类中的方法。之后讲Lambda时会有具体的例子。

    4
  • 兔子
    老师,您好!java.lang.Error这种错误产生的原因是什么样的?jvm对这种Error的处理方式跟Exception一样的吗?如果程序碰到这种情况为了确保程序还能正常运行加上try catch是否就可以了?谢谢!

    作者回复: 应该反过来思考。当碰见没法确保程序正常运行的时候,应用程序应当抛error。否则抛(checked) exception便可以了。

    3
  • 贾智文
    当触发异常的字节码的索引值在某个异常表条目的监控范围内,Java 虚拟机会判断所抛出的异常和该条目想要捕获的异常是否匹配。 这里有点没懂,每层方法的监控范围有可能会重叠吧,只用索引判断不会出现多个情况都满足的情况吗?

    作者回复: 会依照异常表中的前后(上下)顺序来查找,然后被第一个满足条件的异常处理器捕获

    3
  • 无言的约定
    郑老师,问个问题,在执行某个方法时,我不知道在哪会发生异常,这个时候我怎么才能捕获可能产生的异常并存储在日志文件里?

    作者回复: 你可以整个方法用try catch 包住,捕获Throwable并在异常处理器中写日志,并用throw语句重新抛该异常

收起评论
显示
设置
留言
54
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部