06 | JVM是如何处理异常的?
06 | JVM是如何处理异常的?
讲述:郑雨迪
时长11:27大小5.24M
异常的基本概念
Java 虚拟机是如何捕获异常的?
Java 7 的 Suppressed 异常以及语法糖
总结与实践
赞 23
提建议
精选留言(52)
- 钱2018-08-02感谢雨迪! 这节让我终于搞清楚了两个疑惑! 1:使用异常捕获的代码为什么比较耗费性能? 因为构造异常的实例比较耗性能。这从代码层面很难理解,不过站在JVM的角度来看就简单了,因为JVM在构造异常实例时需要生成该异常的栈轨迹。这个操作会逐一访问当前线程的栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常等信息。 虽然具体不清楚JVM的实现细节,但是看描述这件事情也是比较费时费力的。 2:finally是怎么实现无论异常与否都能被执行的? 这个事情是由编译器来实现的,现在的做法是这样的,编译器在编译Java代码时,会复制finally代码块的内容,然后分别放在try-catch代码块所有的正常执行路径及异常执行路径的出口中。展开共 6 条评论178
- 阿坤2018-09-02如果finally有return语句,catch内throw的异常会被忽略,这个从jvm层面怎么解释呢?
作者回复: catch里抛的异常会被finally捕获了,再执行完finally代码后重新抛出该异常。由于finally代码块有个return语句,在重新抛出前就返回了。 你可以利用这篇文章的知识,就着javap的输出,分析一下具体的程序路径
58 - 曲东方2018-08-11throw exception性能差fillstacktrace除了遍历堆栈以外,如果有inline 代码消除等编译优化发生,是不是要先“去优化”完了再fill?要不然可能出现错误堆栈和代码对不上的情况 throw exception估计也会影响jit的优化,进而影响整体性能
作者回复: 思考得很深,赞一个! 即时编译器生成的代码会保存原始的栈信息,以便去优化时能够复原。fillStackTrace也会读取这些信息的,所以不用先去优化再fill。 抛异常本身带来了额外的执行路径。通常如果能够将异常处理器也编译进去,那么不会有太大影响。
共 2 条评论40 - 李二木2018-08-01看完今天的文章有几个疑问 1方法的异常表是包含RuntimeException这种非check类型的异常吧?如果是那么每个方法都有异常表,那么是不是每个异常表中都有像ArrayIndexOutOfBoundsException这类型异常了。这类公共异常是私有还是共享呢 2像catch自定义异常,也会添加的当前方法的异常表里吗? 3 我们常常看到的异常调用栈,这里方法调用信息其实就是弹出方法栈帧吗?展开
作者回复: 1 检查异常这个概念只在源代码中出现。异常表不是声明这段代码所有有可能抛出的异常,而是声明会被捕获的异常。 2 会的 3 栈轨迹 跟 弹出方法栈帧 是两个概念。你可以直接新建一个异常,然后不抛出,直接打印调用栈。这个时候是不会弹出当前栈帧的。
共 2 条评论13 - 吴伟2018-08-01检查异常和非检查异常也就是其他书籍中说的编译期异常和运行时异常?
作者回复: 编译期异常和运行时异常这种划分有点奇怪。 检查异常也会在运行过程中抛出。但是它会要求编译器检查代码有没有显式地处理该异常。非检查异常包括Error和RuntimeException(会不会那本书直译为”运行时异常”?),这两个则不要求编译器显式处理。
9 - 李双迎2018-09-07老师,如果异常构造比较耗时,那么能否通过缓存同一位置相同异常的实例,来解决呢?
作者回复: 理论上是可以的,一般不这么做,原因有两个,一是异常路径无需考虑性能,二是代码可读性。
8 - wuyong2020-08-12```java public class Foo { private int tryBlock; private int catchBlock; private int finallyBlock; private int methodExit; public void test() { try { tryBlock = 0; } catch (Exception e) { catchBlock = 1; } finally { finallyBlock = 2; } methodExit = 3; } } ``` 相当于如下的代码: ```java public class Foo { private int tryBlock; private int catchBlock; private int finallyBlock; private int methodExit; public void test() { try { tryBlock = 0; finallyBlock = 2; } catch (Exception e) { catchBlock = 1; finallyBlock = 2; } catch (Throwable e) { finallyBlock = 2; throw e; } methodExit = 3; } } ```展开7
- Krloy2018-08-08关于try catch的疑问 如果for里面中写 try catch 一百条数据中有1条数据异常 程序正常执行 会返回99条数据 如果for里面不写 try catch写外面 程序正常执行 但是数据返回0 try catch 异常实例构造非常昂贵,因为虚拟机会生成改异常的栈轨迹,改操作会逐一访问改线程栈帧,并记录下各种调试信息。 那么如果我在for中写try catch 的话 会不会每次循环都生成一个异常实例? 上面两种写try catch的方法 哪种要更好点展开共 2 条评论5
- 子清2018-08-01如果在业务层的代码中使用Assert来判断参数是否有问题,然后在调用方捕捉异常,这样会不会耗性能
作者回复: 首先走抛出异常捕获异常的异常执行路径的话,性能肯定是很慢的,因此最好在参数出现问题的概率很小的情况下使用这种方式。 另外,你说的Assert是某个库的工具类,还是assert语句?后者的话,一般只在开发环境中启用吧。
4 - 贾智文2018-08-10当触发异常的字节码的索引值在某个异常表条目的监控范围内,Java 虚拟机会判断所抛出的异常和该条目想要捕获的异常是否匹配。 这里有点没懂,每层方法的监控范围有可能会重叠吧,只用索引判断不会出现多个情况都满足的情况吗?
作者回复: 会依照异常表中的前后(上下)顺序来查找,然后被第一个满足条件的异常处理器捕获
3 - Ennis LM2018-08-01Java 虚拟机会忽略掉异常构造器以及填充栈帧的 Java 方法(Throwable.fillInStackTrace),直接从新建异常位置开始算起。 Java 虚拟机还会忽略标记为不可见的 Java 方法栈帧。 请问老师,填充栈帧的 Java 方法和不可见的 Java 方法栈帧,是什么
作者回复: 前者指Throwable.fillStackTrace以及异常的构造器,后者为Java虚拟机不想让用户看到的栈帧,比如说方法句柄的适配器类中的方法。之后讲Lambda时会有具体的例子。
3 - 王小臭2018-08-01辛苦老师了,这么早更新3
- 兔子2019-11-27老师,您好!java.lang.Error这种错误产生的原因是什么样的?jvm对这种Error的处理方式跟Exception一样的吗?如果程序碰到这种情况为了确保程序还能正常运行加上try catch是否就可以了?谢谢!
作者回复: 应该反过来思考。当碰见没法确保程序正常运行的时候,应用程序应当抛error。否则抛(checked) exception便可以了。
2 - gogo2019-09-27老师您好,请教一个问题,在spring项目中使用了统一异常处理,在service层做一些校验,校验失败时抛出异常,在统一异常处理逻辑里封装异常信息返回给客户端,这种场景自定义异常集成RuntimeException是不是比较好呢?共 2 条评论2
- 左瞳2019-04-22捕捉异常代码性能差是因为需要生成该异常的栈轨迹,就算不捕捉,也会打印该异常的的栈轨迹啊,那性能本来就差啊,何来捕捉异常性能差之说?共 2 条评论2
- 我已经设置了昵称2019-03-14对于实践环节表示看不懂字节码代码,无法理解,老师能不能在后篇解释下前篇遗留的问题2
- 南城风戈2018-08-01沙发2
- Monday2020-07-06动手实践最美丽: Compiled from "Foo.java" public class Foo { public Foo(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public void test(); Code: 0: aload_0 1: iconst_0 2: putfield #2 // Field tryBlock:I 5: aload_0 6: iconst_2 7: putfield #3 // Field finallyBlock:I 10: goto 35 13: astore_1 14: aload_0 15: iconst_1 16: putfield #5 // Field catchBlock:I 19: aload_0 20: iconst_2 21: putfield #3 // Field finallyBlock:I 24: goto 35 27: astore_2 28: aload_0 29: iconst_2 30: putfield #3 // Field finallyBlock:I 33: aload_2 34: athrow 35: aload_0 36: iconst_3 37: putfield #6 // Field methodExit:I 40: return Exception table: from to target type 0 5 13 Class java/lang/Exception 0 5 27 any 13 19 27 any }展开1
- gentleman♥️2019-01-08就是checked异常 一直不try catch ,jvm会怎么个处理流程呢1
- 搬砖的孟达2018-11-28这一篇是看的明白的的一篇。😂1