java虚拟机规范(se8)——java虚拟机结构(五)
2.10 异常
java虚拟机中的异常用Throwable类或者它的子类的实例来表示。抛出一个异常会导致立即非本地(an inmediate nolocal)的控制转移,从发生异常的地方跳到处理异常的地方。
大多数异常是在当前线程执行某些操作时同步发生的。对应的,非同步异常可能发生在程序执行的任何阶段。java虚拟机会由于下面三个原因中的一个抛出异常:
- 执行athrow指令
- java虚拟机同步检测到非正常的执行情况。这些异常不是在程序中的任意点抛出的,而是在执行以下指令后同步抛出:
- 指明异常作为一种可能的结果,例如
- 当指令包含违反Java编程语言语义的操作时,例如在访问数组边界外的元素。
- 程序在加载或者连接的过程中出现错误
- 导致了使用了超过限制的资源,例如使用了太多的内存。
- 指明异常作为一种可能的结果,例如
- 异步异常出现的原因:
- 调用了Thread或者ThreadGroup类的stop方法,或者
- java虚拟机实现出现了内部错误
一个线程调用stop方法会影响另外一个线程,或者一个限制组的所有线程。它们是异步的异常,因为它们可能出现线程执行的任何位置。内部异常被认为是异步的。
Java虚拟机可能允许在抛出异步异常之前执行少量但有限制的指令。这种延迟允许优化代码检测并抛出这些异常,这些异常可以在符合Java编程语言语义的情况下处理。
一个简单的实现可能在每个控件传输指令处轮询异步异常。由于程序的大小是有限的,这就为检测异步异常的总延迟提供了一个界限。由于控制传输之间不会发生异步异常,代码生成器具有一定的灵活性,可以在控制传输之间重新排序计算,以获得更好的性能。The paper Polling Efficiently on Stock Hardware by Marc Feeley, Proc. 1993 Conference on Functional Programming and Computer Architecture, Copenhagen, Denmark, pp. 179–187, is recommended as further reading.
java虚拟机抛出的异常是精确的:当发生控制转移时,在抛出异常之前执行的指令的所有效果必须可以被观察到。异常抛出之后的指令应当是没有被执行过。如果虚拟机进行了代码优化,导致了异常抛出之后的代码可能被执行了,那么必须保证执行这些代码造成的影响对用户是不可见的。
java虚拟机中的每一个方法都会关联0个或者多个异常处理器(exception handlers)。异常处理器描述了其在方法代码中的有效作用范围(通过字节码偏移量来描述)、能处理的异常类型以及处理异常的代码所在的位置。如果导致异常的指令的偏移量在异常处理程序的偏移范围内,并且异常类型与异常处理器处理的异常类的子类相同,则异常与异常处理器匹配。。当抛出异常时,Java虚拟机在当前方法中搜索匹配的异常处理器。如果找到匹配的异常处理器,系统将跳转到异常处理器指定的异常处理代码处执行。
如果当前方法没有产生异常所对应的异常处理器,当前方法调用会立即结束,当前方法中的操作数栈和局部变量表会被丢弃,栈帧被出栈,恢复调用者方法的栈帧。然后这个异常沿着方法调用链,在调用者栈帧等的上下文中被重新抛出。在到达方法调用链顶层前没有找到合适的异常处理器,异常抛出的线程将被终止。
方法的异常处理器的顺序在搜索匹配时重要的。在class文件中,每个方法的异常处理器时存在表中。在运行时,当异常抛出时,java虚拟机按照顺序搜索当前方法的异常处理器,顺序是根据它们出现在class文件表的位置,从表的起始处开始。
请注意,Java虚拟机不强制嵌套或对方法的异常表项进行任何排序。所以java语言中对异常处理的语义,实际上是通过编译器适当安排异常处理器在表中的顺序来协助完成的。在class文件中定义了明确的异常处理器查找顺序,才能确保无论class文件时通过何种途径产生的,java续集及执行时都能有一致的行为表现。