专利名称:一种工作于混合模式执行引擎中的异常处理方法
技术领域:
本发明属于Java编译运行环境设计技术领域,具体涉及Java不同的执行引擎混合工作时的异常处理方法。
背景技术:
异常处理机制是现代编程语言中普遍采用的一种提高程序可靠性的方法。作为目前应用最广泛的面向对象编程语言,Java也将异常处理机制视为语言规范的一个重要方面。异常通常分为两种类型,运行时异常(Runtime Exc印tion)和检查异常(Checked Exc印tion)。前者为程序运行过程中可能抛出的不确定异常,诸如空指针(Null Pointer), 数据下标越界(Arraylndex Out Of Bounds)等;后者为程序员在编写程序代码时指定的某些函数可能抛出的异常,如IOException等。异常处理通常也分为两个阶段,异常抛出 (Throw)与异常捕获(Catch)阶段。前一阶段将构造即将抛出的异常对象并启动后一阶段, 后一阶段依据前一阶段提供的信息,操作虚拟机异常处理相关数据结构,对抛出的异常处理器进行搜索并做相应的执行流跳转。虚拟机存在多个执行引擎,如解释器、即时编译器。Java应用程序一次执行通常只发生在一个引擎上。现如今,在某些虚拟机中已经存在着多种执行引擎混合执行的工作模式。混合执行能使虚拟机同时获得解释器资源要求少和即时编译器执行更快的优点,从而增加了 Java应用程序的可使用范围,更易于Java在多样的资源有限的嵌入式等设备上扩张。通常,异常处理机制有不同的实现方式,但是这些实现只是针对非混合执行的工作模式。新的执行模式需要引入新的异常处理机制以保证Java应用程序在混合模式执行下的可靠性与正确性。因此,设计一种能够支持混合执行模式的Java异常处理机制,并将其正确地实现是当务之急,也显得尤为重要。
发明内容
本发明的目的在于提出一种能保证Java应用程序在混合模式执行下的可靠性与正确性的异常处理方法。由于运行于动态编译与解释执行混合执行模式下的Java虚拟机有几种不同模式生成代码交互运行,而非解释器模式下的单一解释模式执行,其涉及到的函数栈帧,除了解释器模式下的Java函数栈帧,还包括了本地函数栈帧以及动态编译生成函数栈帧,且各个执行引擎在执行函数时栈帧结构、PC值信息等不尽相同。因此,要在混合模式下实现异常的捕获,需要在原有的传统解释模式下执行的异常处理步骤的基础上,进一步考虑在不同引擎上执行的代码相互调用边界,以保证栈帧回退能正确进行。因此,在混合模式执行下的异常处理机制主要分为如下两步 一、寻找最先匹配的Catch块
这部分操作的目的主要在于定位“异常产生的位置”,然后寻找到相匹配的Catch块。须注意此位置只与Java代码相关。比如Throw语句导致的异常抛出,此语句的所在位置即为此异常的位置;以及在JNI本地函数中产生了异常,则此本地函数被调用的位置即为此异常产生的位置。虚拟机在执行过程中,当遇到0P_THR0W等抛出异常的字节码指令,或是程序满足了抛出运行时异常的条件,又或是在本地函数中发生了异常的“伪抛出”,执行引擎就会在适当的时候,初始化并存储此异常对象,然后将该异常对象实际“抛出”,并启动“捕获”过程,也就是在当前函数调用轨迹上,寻找到与此异常所匹配的最近的异常处理代码,并跳转到此Catch块中。解释器在解释0P_THR0ff/0P_THR0ff_VERI FI CAT 10N_ERR0R时,先获取异常对象,当前PC值以及当前执行的Java函数的栈帧指针等值,随后解释器依据当前PC值,从当前函数栈帧(即THROW所在函数)开始搜索该PC值是否出现在某一 Try块的作用范围内。若找到Throw所在的Try块,则进一步拿异常对象与其所匹配的Catch块所处理的异常类型进行比对,若符合,则说明找到了相应的Catch块,若无符合的情况,则说明相应的Catch处理逻辑不在当前关注的Java函数中,则进行栈的回退。当Java虚拟机在混合模式执行引擎执行下时,因为会有部分Java函数被即时编译并执行,其函数栈帧处在本地栈上,故而需要将这些本地函数栈帧同解释器执行的 Java函数栈帧进行“串联”,以提供一个完整的当前执行上下文(Execution Context, or Execution Path)视图。当进行解释模式执行时,解释器执行引擎会在当前的Java栈帧中分配出一块内存区域,称为保存区。每次会在这块保存区中记录下上个函数的栈帧指针、PC 值等信息,继而为之后的栈回退做准备,同时每次从本地栈帧通过JNI调用进入解释器模式时,都会在Java栈上插入一个中断栈帧,用来标识Java执行流被中断。因此在解释模式下,如上措施可以保证Java函数栈帧之间的“串联性”(如附图1所示)。因此,即时编译器在编译Java函数生成本地栈帧时,需要在生成的本地函数栈上也分配一块类似Java栈帧中的保存区的区域,用以记录调用者函数的栈帧指针值和调用语句的PC值;在被编译执行的函数去调用解释模式的函数时,在此函数对应的Java函数栈帧上的中断区域里,亦需记录下动态编译函数的本地栈帧指针及调用发生时的本地PC值。 如此,便可以使得本地函数栈帧同Java函数栈帧相“串联”起来。当采用解释模式时,异常产生的位置是由于执行Java字节码产生的;采用混合模式执行,异常产生的位置还有可能在动态编译生成的二进制代码里。故而在动态编译代码时,与解释器类似,也需要创建Try块表(Try Table),用以存储每个编译执行的函数的 Try-Catch信息,即每个Try块作用的二进制代码范围、每个Catch块所对应的二进制代码地址。这样,当异常产生于二进制代码层面上的位置时,就能按照异常处理的两个步骤操作判断异常产生的位置所处的Try块,从而进一步再找到其所对应的Catch块处理代码位置并跳转。根据调用者及被调用者的种类不同,保存区的存放位置也会有所不同,有如下几种情况
1)本地函数调用解释器函数
会通过函数调用进入解释模式,主要逻辑同正常进入解释器模式逻辑相同,将在Java 栈上插入一个中断栈帧,表示调用者为本地函数。
2)动态编译生成函数调用解释器函数
同上一种情况类似,所不同的是,对于中断栈帧的处理。为了同本地函数调用解释器函数时的中断栈帧有所区别,我们需要在中断栈帧中,另外记录一下调用者究竟是动态编译生成的函数还是本地函数。3)被调用者为本地函数
主要逻辑同正常的解释器模式执行一致,仍然为此本地函数在Java栈帧结构上分配一块保存区,记录调用者的栈帧指针值及PC值。若调用者为解释器函数,则栈帧指针及PC 则分别指向解释器中的栈帧以及解释器中的指令位置;若调用者为动态编译生成函数,则栈帧指针及PC则分别指向本地栈中的栈帧以及真实PC寄存器的值。4)被调用者为动态编译生成函数
与上种情况所不同的是,为动态编译生成函数所分配的保存区位于本地栈帧之上。这样一来,便可以获得处理异常时的调用路径,然后对此路径上的函数,逐层往上考察各层函数,通过每个函数栈帧中的Try-Catch信息,就可定位出“异常产生的位置”存在于哪个Try块中,从而可进一步得知此Try块所对应的各个Catch块,之后再进行进一步的异常对象与各Catch块所捕捉的异常的类型的比对,最终可定位出捕获此异常的Catch 逻辑的位置。二进行栈回退,并跳转到异常捕获代码
在通过函数调用获得某一异常所匹配的Catch块及Catch块所在函数的信息之后,接下来需要将执行流程改变到此Catch块上,同时,栈的信息也要层层回退到此Catch块所在函数所对应的栈帧。对于执行流程的改变,如果Catch块所在函数运行在解释模式下,则在找到所匹配的Catch块后,解释器获得其所在的函数的栈帧指针值以及Catch语句首条指令地址,随后,解释器操作栈指针使Catch块所在函数处于栈顶,同时将PC值设置为Catch 块所在函数的起始地址+偏移的位置,然后就是正常的取址、分发、解释的执行流程了 ;若其所在函数为动态编译生成的代码,则通过调整物理寄存器PC的值,使CPU执行此段代码即可。但在执行流程改变之前,需要完成栈的回退操作,这是为了保证稍后执行的代码的上下文信息(真实寄存器、虚拟寄存器、栈帧)的正确性。在回退的过程中,需要根据异常发生时所在函数的类别,而采取不同的操作 A、异常发生在动态编译生成函数中
通过函数调用,沿着调用路径向上寻找所匹配的Catch块处理函数,此时只关注调用路径上的动态编译生成函数,若找到某一动态编译生成函数,其包含着所匹配的Catch块, 则将真实的栈帧指针、栈顶指针切换到此函数所对应的栈帧指针以及栈顶指针,并将PC调整为Catch块的起始地址;若在寻找过程中,遇到了 Java栈帧(中断栈帧或是Java函数对应的Java栈帧),说明此异常不能被当前已经遍历过的动态编译生成函数所捕获,那么,该异常一定是被再上层的函数所捕获的,此时,将真实的栈帧指针以及栈顶指针替换为此最顶端的动态编译生成函数所保存的栈帧指针以及栈顶指针的值,然后取出动态编译生成函数所对应的返回地址,使其直接返回即可。上述函数返回至不同执行引擎的边界,该边界称作函数调用桥(Call Bridge)。函数调用桥在不同引擎切换时,负责记录调用者与被调用者的信息,以及上下文信息的一致性与完整性。通过调用桥,可以获知函数的调用者的类型。若调用此动态编译生成函数的父函数为本地函数,则它会带着异常继续执行,直到某个点,虚拟机检查到此异常的存在,然后将异常抛回给调用该本地函数的引擎使回退过程得以继续;若调用此动态编译生成函数的父函数为解释执行的函数,函数调用桥会检查此异常,并借助记录的调用者信息,复原解释器状态,从而将此异常交由以下第二种策略所描述的解释执行模式处理方式继续处理。 故而采用这种策略,既保证了 Java中遇到本地函数时对于异常处理的规范,又确保了完整无缺地处理了异常。B、异常发生在解释执行函数中
此时的处理逻辑同正常的解释模式执行中的处理逻辑相似,在扫描当前调用路径上, 从当前函数起往上的所有连续的解释执行的函数。若扫描到中断栈帧(本地函数)或是扫描到动态编译生成函数,则停止扫描。若在扫描过程中,寻找到某一解释执行函数,其包含相应的Catch块处理函数,则将虚拟栈帧指针值恢复为此解释执行的函数对应的栈帧指针值,同时将虚拟PC设置为此 Catch块处理函数的第一条指令的地址,之后便将在解释模式下进行取址分发。若在扫描过程中,没有寻找到包含相匹配Catch块的解释模式下的函数,则一定代表扫描时遇到了中断栈帧(动态编译生成函数或是本地函数调用解释模式的函数时压入的),则此时会退出解释器引擎而进入执行边界的函数调用桥。之后,同A中所述类似,借助函数调用桥中维护的信息,若调用此函数的父函数为本地函数,则它会带着异常继续执行,直到某个点,虚拟机检查到此异常的存在,然后将异常抛回给调用该本地函数的引擎使回退过程得以继续;若调用者为动态编译生成函数,则需要在它调用解释器函数之后,对当前线程上的异常进行检查,如发现有异常,则转用第一种策略,继续处理此异常。本发明的有益效果是本发明针对现有的混合执行引擎工作模式下的Java虚拟机中的异常处理问题,提出了一个结合了不同执行引擎异常处理特点的异常处理方法,实现了 Java虚拟机采用动态编译和解释器模式混合执行模式下的异常处理策略。
图1 Java栈与本地栈结构栈回退示图。图2算法流程图。图3示例栈结构示图。
具体实施例方式本发明设计并实现了上述的工作于混合模式执行引擎中的异常处理机制,本节对该框架的具体实施作一个详细的介绍。介绍以实例方式进行,示例主体代码如附录所示。该程序在main函数中,直接调用nestFimc,该函数将由动态编译器进行动态编译执行;nestFimc函数将调用interpFimc函数,而该函数将由解释器进行解释执行;最后, interpFunc 函数调用 ExceptionThrowFunc。ExceptionThrowFunc 也是由动态编译器进行动态编译执行,在该函数中,将会抛出一个运行时异常,以供最外层main函数中的 1Try-Catch 捕获。
遇到异常抛出时,将按照如下算法进行异常处理,算法的过程如下 Begin
(1)if检查异常抛出
(2)将线程中表示异常发生的标志位置上
(3)for取出当前函数Method的栈帧do
(4)扫描函数中的所有的异常处理代码,找出和当前异常相匹配的异常处理块
(5)if 找到 do
(6)跳出循环
(7)else
(8)根据当前栈帧的类型(Java函数栈帧,动态编译生成函数栈帧)进行不同策略的栈回退,找到上一个函数的栈帧
(9)if取出栈帧为中断栈帧do
(10)将线程异常位依旧置上,抛给上层进行处理
(11)end if
(12)end for
(13)找到了匹配的异常处理块,根据当前栈帧的类型,采取不同的策略更新PC 值,跳转至Catch块的首指令处
(14)end if End。示例中,当虚拟机检测到异常发生时,取出当前栈帧,为Exc印tionllirowFunc函数栈帧,该函数为动态编译生成函数,查找当前函数,并未找到相应的异常处理块,故而需要进行栈回退,根据保存区中的信息,取出上一个栈帧,发现为Java栈帧,即interpFimc函数的栈帧,查找相应的异常处理块,未能匹配,再次进行Java栈帧上的栈回退,取出上一个栈帧发现为中断栈帧,继续栈回退,取出当前栈帧,为nestFimc,查找相应的异常处理块,未能匹配,取出上一个栈帧,发现仍为动态编译生成函数栈帧,故而继续在本地栈帧上的进行栈回退,取出main函数的函数栈帧,查找相应的异常处理块,匹配成功,更新PC值,进入相应的异常处理块进行继续执行。至此,示例函数中产生的异常被成功捕获。附录
0001:public class Example {
0002 public static void nestFunc(){
0003System, out. println("This is a nest func!〃);
0004interpFunc ();
0005
0006
0007 public static void ExceptionThrowFunc (){
0008throw new RuntimeException("Exception example!
0009
00100011 public static void interpFunc (){
0012ExceptionThrowFuncO ;
0013 }
0014
0015 public static void main(String args[]) {
0016 try{
0017nestFunc ();
0018catch(Exception ex) {
0019System, out. println("Exception catched!〃);
0020ex.printStackTrace();
0021 } 0022 }
0023 }
权利要求
1. 一种工作于混合模式执行引擎中的异常处理方法,其特征在于分为如下两步一、寻找最先匹配的Catch块虚拟机在执行过程中,当遇到0P_THR0W抛出异常的字节码指令,或是程序满足了抛出运行时异常的条件,或是在本地函数中发生了异常的“伪抛出”,执行引擎在适当的时候,初始化并存储此异常对象,然后将该异常对象实际“抛出”,并启动“捕获”过程,即在当前函数调用轨迹上,寻找到与此异常所匹配的最近的异常处理代码,并跳转到此Catch块中;解释器在解释0P_THR0ff/0P_THR0ff_VERI FI CAT 10N_ERR0R时,先获取异常对象、当前PC 值以及当前执行的Java函数的栈帧指针值,随后解释器依据当前PC值,从当前函数栈帧即THROW所在函数开始搜索该PC值是否出现在某一 Try块的作用范围内;若找到Throw所在的Try块,则进一步拿异常对象与其所匹配的Catch块所处理的异常类型进行比对,若符合,则找到了相应的Catch块,若无符合的情况,相应的Catch处理逻辑不在当前关注的 Java函数中,则进行栈的回退;当Java虚拟机在混合模式执行引擎执行下时,将这些本地函数栈帧同解释器执行的 Java函数栈帧进行“串联”,以提供一个完整的当前执行上下文视图;当进行解释模式执行时,解释器执行引擎在当前的Java栈帧中分配出一块内存区域,称为保存区;每次在这块保存区中记录下上个函数的栈帧指针、PC值信息,为之后的栈回退做准备,同时每次从本地栈帧通过JNI调用进入解释器模式时,在Java栈上插入一个中断栈帧,用来标识Java执行流被中断;即时编译器在编译Java函数生成本地栈帧时,在生成的本地函数栈上也分配一块类似Java栈帧中的保存区的区域,用以记录调用者函数的栈帧指针值和调用语句的PC值;在被编译执行的函数去调用解释模式的函数时,在此函数对应的Java函数栈帧上的中断区域里,亦记录下动态编译函数的本地栈帧指针及调用发生时的本地PC值,使得本地函数栈帧同Java函数栈帧相“串联”起来;当采用解释模式时,异常产生的位置是执行Java字节码产生的;采用混合模式执行, 异常产生的位置可能在动态编译生成的二进制代码里;故而在动态编译代码时,需要创建 Try块表,用以存储每个编译执行的函数的Try-Catch信息,即每个Try块作用的二进制代码范围、每个Catch块所对应的二进制代码地址;这样,当异常产生于二进制代码层面上的位置时,按照异常处理的两个步骤操作判断异常产生的位置所处的Try块,从而进一步再找到其所对应的Catch块处理代码位置并跳转;根据调用者及被调用者的种类不同,保存区的存放位置也会有所不同,有如下几种情况1)本地函数调用解释器函数通过函数调用进入解释模式,主要逻辑同正常进入解释器模式逻辑相同,将在Java栈上插入一个中断栈帧,表示调用者为本地函数;2)动态编译生成函数调用解释器函数为了同本地函数调用解释器函数时的中断栈帧有所区别,在中断栈帧中,另外记录一下调用者究竟是动态编译生成的函数还是本地函数;3)被调用者为本地函数主要逻辑同正常的解释器模式执行一致,仍然为此本地函数在Java栈帧结构上分配一块保存区,记录调用者的栈帧指针值及PC值;若调用者为解释器函数,则栈帧指针及PC 则分别指向解释器中的栈帧以及解释器中的指令位置;若调用者为动态编译生成函数,则栈帧指针及PC则分别指向本地栈中的栈帧以及真实PC寄存器的值;4)被调用者为动态编译生成函数与上种情况所不同的是,为动态编译生成函数所分配的保存区位于本地栈帧之上;这样,便获得处理异常时的调用路径,然后对此路径上的函数,逐层往上考察各层函数,通过每个函数栈帧中的Try-Catch信息,定位出“异常产生的位置”存在于哪个Try块中,从而进一步得知此Try块所对应的各个Catch块,之后再进行进一步的异常对象与各 Catch块所捕捉的异常的类型的比对,最终定位出捕获此异常的Catch逻辑的位置;二、进行栈回退,并跳转到异常捕获代码在通过函数调用获得某一异常所匹配的Catch块及Catch块所在函数的信息之后,将执行流程改变到此Catch块上,同时,栈的信息也要层层回退到此Catch块所在函数所对应的栈帧;对于执行流程的改变,如果Catch块所在函数运行在解释模式下,则在找到所匹配的 Catch块后,解释器获得其所在的函数的栈帧指针值以及Catch语句首条指令地址,随后, 解释器操作栈指针使Catch块所在函数处于栈顶,同时将PC值设置为Catch块所在函数的起始地址+偏移的位置,然后进行正常的取址、分发、解释的执行流程;若其所在函数为动态编译生成的代码,则通过调整物理寄存器PC的值,使CPU执行此段代码即可;但在执行流程改变之前,需要完成栈的回退操作;在回退的过程中,需要根据异常发生时所在函数的类别,而采取不同的操作A、异常发生在动态编译生成函数中通过函数调用,沿着调用路径向上寻找所匹配的Catch块处理函数,此时只关注调用路径上的动态编译生成函数,若找到某一动态编译生成函数,其包含着所匹配的Catch块, 则将真实的栈帧指针、栈顶指针切换到此函数所对应的栈帧指针以及栈顶指针,并将PC调整为Catch块的起始地址;若在寻找过程中,遇到Java栈帧,说明此异常不能被当前已经遍历过的动态编译生成函数所捕获,那么,该异常一定是被再上层的函数所捕获的,此时,将真实的栈帧指针以及栈顶指针替换为此最顶端的动态编译生成函数所保存的栈帧指针以及栈顶指针的值,然后取出动态编译生成函数所对应的返回地址,使其直接返回即可;上述函数返回至不同执行引擎的边界,该边界称作函数调用桥;函数调用桥在不同引擎切换时,负责记录调用者与被调用者的信息,以及上下文信息的一致性与完整性;通过函数调用桥,可以获知函数的调用者的类型;若调用此动态编译生成函数的父函数为本地函数,则它带着异常继续执行,直到某个点,虚拟机检查到此异常的存在,然后将异常抛回给调用该本地函数的引擎使回退过程得以继续;若调用此动态编译生成函数的父函数为解释执行的函数,函数调用桥检查此异常,并借助记录的调用者信息,复原解释器状态,从而将此异常交由以下第二种策略所描述的解释执行模式处理方式继续处理;B、异常发生在解释执行函数中此时的处理逻辑同正常的解释模式执行中的处理逻辑相似,在扫描当前调用路径上, 从当前函数起往上的所有连续的解释执行的函数;若扫描到中断栈帧或是扫描到动态编译生成函数,则停止扫描;若在扫描过程中,寻找到某一解释执行函数,其包含相应的Catch块处理函数,则将虚拟栈帧指针值恢复为此解释执行的函数对应的栈帧指针值,同时将虚拟PC设置为此Catch 块处理函数的第一条指令的地址,之后便将在解释模式下进行取址分发;若在扫描过程中,没有寻找到包含相匹配Catch块的解释模式下的函数,则一定代表扫描时遇到了中断栈帧,则此时退出解释器引擎而进入执行边界的函数调用桥;之后,同A中所述类似,借助函数调用桥中维护的信息,若调用此函数的父函数为本地函数,则它带着异常继续执行,直到某个点,虚拟机检查到此异常的存在,然后将异常抛回给调用该本地函数的引擎使回退过程得以继续;若调用者为动态编译生成函数,则需要在它调用解释器函数之后,对当前线程上的异常进行检查,如发现有异常,则转用第一种策略,继续处理此异常。
全文摘要
本发明属于Java编译运行环境设计技术领域,具体为一种工作于混合模式执行引擎中的异常处理方法。本发明针对现有Java虚拟机中存在的多种执行引擎混合工作模式,根据不同执行引擎各自不同的特性制定相应的异常处理策略,从而提出一种新型的异常处理方法,包括寻找最先匹配的Catch块,通过函数调用获得某一异常所匹配的Catch块及Catch块所在函数的信息,将执行流程改变到此Catch块上,同时,栈的信息也要层层回退到此Catch块所在函数所对应的栈帧。本发明解决了多种执行引擎下的Java虚拟机中的异常处理问题。
文档编号G06F9/44GK102262537SQ20111020455
公开日2011年11月30日 申请日期2011年7月21日 优先权日2011年7月21日
发明者张源, 彭智俊, 杨珉 申请人:复旦大学