jvm异常处理
仅作为作者学习笔记
前言
仅作为作者学习笔记
一、异常的一些基本概念
- 在 Java 语言规范中,所有异常都是 Throwable 类或者其子类的实例。Throwable 有两大直接子类。第一个是 Error,涵盖程序不应捕获的异常。当程序触发 Error 时,它的执行状态已经无法恢复,需要中止线程甚至是中止虚拟机。第二子类则是 Exception,涵盖程序可能需要捕获并且处理的异常。如下图
- Exception 有一个特殊的子类 RuntimeException,用来表示“程序虽然无法继续执行,但是还能抢救一下”的情况。前边提到的数组索引越界便是其中的一种。
- 非检查异常:数据在代码编译阶段,不会被虚拟机抛出的异常,而是需要代码执行到指定行才会抛出的异常。
- 检查异常:需要在程序中显示的捕获,或者在方法中通过关键字throws标注抛出。比如自定义的异常。
- RuntimeException 和 Error 属于 Java 里的非检查异常(unchecked exception)。其他异常则属于检查异常(checked exception)。
二、java虚拟机是如何捕获异常的?
Java 字节码中,每个方法对应一个异常表。当程序触发异常时,Java 虚拟机将查找异常表,并依此决定需要将控制流转移至哪个异常处理器之中。Java 代码中的 catch 代码块和 finally 代码块都会生成异常表条目。
1:使用异常捕获的代码为什么比较耗费性能?
因为构造异常的实例比较耗性能。这从代码层面很难理解,不过站在JVM的角度来看就简单了,因为JVM在构造异常实例时需要生成该异常的栈轨迹。这个操作会逐一访问当前线程的栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常等信息。
2:finally是怎么实现无论异常与否都能被执行的?
这个事情是由编译器来实现的,现在的做法是这样的,编译器在编译Java代码时,会复制finally代码块的内容,然后分别放在try-catch代码块所有的正常执行路径及异常执行路径的出口中。
3.如果 catch 代码块捕获了异常,并且触发了另一个异常,那么 finally 捕获并且重抛的异常是哪个呢?
答案是后者。也就是说原本的异常便会被忽略掉,这对于代码调试来说十分不利。