JVM异常与Finally

异常

Java虚拟机通过异常表,实现了对异常的处理。

Java虚拟机中的异常由Throwable或其子类的实例来表示。

抛出异常本质,实际上是程序控制权的一种立即/即时【immediate】的、非局部的【nonlocal 】的转移----从异常抛出点转移到异常处理点。

异常分为:

  • 同步异常【synchronously exception】:绝大多数的异常是由于当前线程执行的某个操作导致的,这也被成为同步异常
  • 异步异常【asynchronous exception】:可能发生在程序执行的任何时刻的异常,称为异步异常

Java虚拟机抛出异常的原因有以下三个:

(1)athrow指令执行了
(2)Java虚拟机同步检测到程序发生了非正常的执行情况。这些异常不是在程序中的任意点抛出,而是在执行以下指令之后同步抛出:

  • 导致对资源的某些限制被超过,比如当使用太多内存时。
  • 程序执行的操作可能会引发异常,比如访问数组边界之外的元素,或程序在加载和连接时出错。

(3)以下原因会导致异步异常:

  • 调用Thread或ThreadGroup的stop方法:某个线程可以调用另一个线程的stop方法,这将会影响另一个线程或线程组中的所有线程。该异常是异步的,因为它们可能发生在其他线程执行的任何时刻。
  • Java虚拟机实现中出现内部错误。

由Java虚拟机中的每个方法都会配有零个或多个异常处理器【exception handlers】。

Exception table:
From    To      Target      Type
0       4       5           Class TestExc

异常处理器的组成包括:

  • 异常处理器在方法代码中的有效作用范围(通过字节码偏移的范围来描述),对应于From和To,前闭后开!!
  • 异常处理器能够处理的异常类型,对应于Type
  • 处理该异常的代码的位置,对应于Target

判断某个异常处理器能否处理某个具体的异常:

  1. 首先检查异常抛出的代码的偏移量,是否处于异常处理器的From-To之间,如果否,那么该异常处理器无法处理该异常
  2. 判断异常的类型是否是异常处理器声明的可以处理的异常类型【即Type】或其子类。如果否,那么该异常处理器无法处理该异常
  3. 将代码控制权转移到异常处理器所指定的异常处理代码分支处【即Target】。

如果在当前方法中没有找到处理该异常的异常处理器,则当前方法调用将突然结束【completes abruptly,突然结束的方法调用永远不会向调用者返回值!!】。在方法突然结束时,会丢弃当前方法的操作数堆栈和局部变量表,并弹出其栈帧,然后恢复调用方法的栈帧。未处理的异常会在发起调用的方法的栈帧中重新抛出。并在整个方法调用链中重复该处理逻辑。如果抵达方法调用链的顶端,仍然没有找到合适的异常处理器,则抛出异常的线程的执行将终止。

方法的异常处理程序搜索匹配项的顺序很重要。在类文件中,每个方法的异常处理器都存储在一个表中【异常表】。在运行时,当抛出异常时,Java虚拟机按照异常处理器在类文件中出现的顺序,从前至后顺序检索。

Finally

JVM通过jsr、jsr_w、ret字节码指令和异常表,实现了Finally字句。

Finally在方法内部的表现很像“微型子例程”,在将控制转移到try语句之外之前,无论转移是正常的还是突然的(抛出了异常),都必须首先执行finally子句。finally字句结束后(指finally字句的最后一条语句正常执行完毕,不包括抛出异常、return、break、continue等情况),隶属于这个finally字句的微型子例程执行“返回”操作,程序会继续在第一次调用微型子例程的地方继续执行后面的语句。

有四种方式,可以让程序退出try语句:

  • 语句块内的语句正常执行结束
  • 通过return语句退出方法
  • 执行brean或continue
  • 抛出异常
操作码 操作数 解释
jsr n 跳转子例程,即跳转到偏移量为n的指令处执行。jsr指令在跳转之前,会将其下一条指令的地址(即n+1)压入到操作数栈中。
ret x 返回到局部变量表中index = x处所指向的指令地址处

jsr指令是使虚拟机跳转到微型子例程的操作码。jsr指令使用一个双字节的操作数,这个操作数指出微型子例程的字节码偏移量,Java还支持另一个操作码:jsr_w,它支持更长的操作数(4个字节)。当虚拟机遇到jsr或jsr_w操作码,他会将下一条指令的地址压入操作数栈,然后从微型子例程的开始处继续执行,微型子例程执行完后,会调用ret指令,该指令执行从微型子例程返回的操作。ret只有一个操作数,这个操作数是一个局部变量的索引,该索引处存储的是返回地址。

jsr指令并不会调用Java方法,他只是跳转到相同方法中不同的操作码处。同样,ret指令也不会令Java方法返回,它只能使虚拟机调回到相同方法中调用jsr指令之后的位置。

你可能感兴趣的:(JVM,JVM)