一种基于字节码文件重构的Java类在线更新方法

文档序号:6335864阅读:166来源:国知局

专利名称::一种基于字节码文件重构的Java类在线更新方法
技术领域
:本发明属于计算机应用领域,具体是一种基于字节码文件重构的高效Java类在线更新方法。
背景技术
:软件是人的脑力劳动创造出来的产物。世界上没有完美的软件,开发者也很难一蹴而就,完成交付一个不需要改进的软件系统。软件总需要开发者对其进行不断维护,例如bug的修复、效率的改进、功能的增强以及代码重构等。传统软件更新需要终止正在运行的软件,然后某些场合,尤其比较重要的软件系统,终止正在运行的软件的代价很大。为此,一种在线软件更新技术就显的十分重要。基于JVM(Java虚拟机)的软件更新系统,由于虚拟机的易于修改特性而具有很高的研究价值。在机器上运行Java程序要经过编写源码,编译源码,启动JVM加载类文件运行等步骤。Java语言主要是由类组成,而类主要是由域和方法定义组成。在JVM加载类文件后,JVM将把读取的字节码文件映射成相应的运行时刻元数据。这是对二进制文件在运行时刻的第一层处理。当需要执行方法时,JVM会调用他的在线编译器将字节码编译成相应机器码,CPU读取这些机器指令执行相应功能。JVM会在内存分配的堆区中创建对象,对象对应的类型信息和类元数据相关,这些信息主要描述了对象空间各偏移对应的域,以及该对象能够运行的实例方法。动态更新系统为保持更新的一致性,定义了一个基本语义更新前不运行新代码,更新后不运行旧代码。除此之外在更新过程中也要保证基本的类型安全。动态更新系统一般需要一个更新准备步骤,主要是通过分析比较新旧版本程序发生的改变并将这些信息输出到文件中,同时程序员也可能需要编写一些更新补丁。根据新旧版本Java类发生的改变不同,在分析阶段主要将需要处理的Java类划分为如下三种1.当类的父类、接口集合、域集合、方法集合发生改变时,需要将该类利用JVM的动态加载机制重新加载进虚拟机,将类定义以及域定义、方法定义映射成相应的运行时刻元数据。这种类定义发生改变的更新称为类更新。2.当类定义没有发生改变仅有方法的实现发生改变时,这时只要为需要将方法对应的VM元数据进行更新即可。这种类定义未变化但存在方法字节码改变更新称为方法体更新。3.当类定义和方法字节码没有任何改变,但由于存在方法引用了类更新的类,则需要将相应方法的机器码进行更新。这种字节码没有发生改变,但引用了类更新类的方法更新称为引用方法更新。不同的更新会导致运行时刻元数据发生更改,而动态更新就是要替换运行时刻元数据。对上述分析得到的不同更新,在运行在线更新时需要采取不同的处理方式1.类更新类更新使得运行时刻类元数据需要重新生成,与此同时方法字节码也发生改变,方法机器码也随之改变。由于类型的唯一性,在替换新类是需要将旧类定义从运行时刻类型管理机制中移除。具体为1运行系统中移除旧类元数据一2动态加载新类一3生成新的类元数据、域元数据、方法元数据一4编译方法2.方法体更新方法体更新使得方法的字节码发生改变,与此同时方法的字节码也发生改变。具体为1替换方法元数据中的字节码部分一2重新编译方法3.引用方法更新引用方法的机器码中有部分指令的操作数发生改变,这个时候一种简单的解决方法就是重新编译整个方法,但这显然不是最佳方法。具体为1重新编译方法。更新需要程序运行到一个安全点,而安全点需要能够停止所有用户线程,并扫描用户线程,判定线程运行栈中没有受限方法。机器码发生改变的方法是受限方法,此外,用户也可以根据程序逻辑指定受限方法。当某方法被删除后,为了确保更新语义,这些方法也要被标记为受限的方法。引用方法更新的机器码需要修复,一般做法是重新编译。这类方法的特点一是字节码没有发生改变,二是引用了类更新类。虽然它的字节码没有改变,引用的方法也皆存在于新旧版本中,但由于JVM的动态类加载机制会将新类对应的元数据分配到新的偏移,这就导致引用方法更新的方法的机器码需要重新编译了。如果可以确保这种偏移更新前后不发生改变,那么引用方法更新导致的重编译就可以免去,提高更新的效率。本文提出一种方法结合分析并重构字节码二进制文件,在运行时刻调整动态加载类的机制,来达到减少引用方法更新带来的重编译。
发明内容本发明所要解决的技术问题是提供一种基于字节码文件重构的Java类在线更新方法,该方法通过在运行时刻调整动态加载类的机制,降低运行时刻元数据的变更,减少了引用方法更新带来的重编译,从而提高了更新的效率。本发明所述的一种基于字节码文件重构的Java类在线更新方法,其主要包含以下步骤1)分析新旧版本程序中二进制字节码文件,找出类更新、类方法体更新、类引用方法更新所针对的类;2)根据分析结果在运行时刻找寻合适的更新点;3)在运行时刻,根据类发生的更新,对其做相应的更新操作,具体为3.1)对于类更新的类,将新类替换旧类;3.2)对于类方法体更新的类,将其定义的方法重新读取并初始化可用;3.3)对于类引用方法更新的类,将存在相应引用的方法重新编译;4)将堆区中类型改变的类实例转换成新类型实例。6上述步骤1)的过程为基于JVM的更新的分析工具对所有类遍历两遍,第一次遍历1.1)根据旧版本中类在新版中出现的情况,对旧版本类进行分类,标记出减少的类和新旧版本中都存在的类;对新旧版本中都存在的类,进一步标记出类定义变化和类定义不变化的类;1.2)根据旧版本中类存在的类中方法在新版中出现的情况,标记出减少的方法、都存在但方法体变化的方法,以及都存在且方法体不变的方法;第二次遍历1.3)对那些存在于新旧版本中且方法体保持不变的方法,标记出引用类定义改变的类的方法;标记输出结果1.4)将步骤1.1)中减少的类、类定义变化的类中的所有方法,类定义不变的类中方法体发生改变的方法、方法体不变但引用类定义改变的类的方法标记为受限方法;15)将步骤1.4)中标记出的受限方法中,字节码不变的方法标记为字节码不变的受限方法。上述步骤1.1)标记类的具体步骤为在旧版本中取一类C,判断其在新版本中是否存在同样的C类,若不存在则标记C为减少的类;若存在,则进一步比较类的定义是否发生改变,而判断类定义是否发生改变的步骤为1.1.1)判断二进制文件中的类基本信息是否发生改变,若父类为减少的类则标记该类类继承关系变化;1.1.2)判断并标记域存在域在运行时刻偏移是否能保持不变,根据相应VM平台的实例域的布局计算出实例域在堆区对象实例空间中的偏移,该偏移与域出现在二进制文件中的顺序相关,对编译器生成的二进制字节码文件进行重构,调整域出现的顺序;1.1.3)在1.1.2)调整布局之后,必要时判断并标记出类的实例大小是否发生改变;1.1.4)判断并标记方法存在方法在运行时刻偏移是否能保持不变,根据相应VM平台实例方法偏移算法计算出偏移,该偏移与方法出现在二进制文件中的顺序有关,必要时对编译器生成的二进制字节码文件进行重构,调整方法出现的顺序;1.1.5)若有以上之一发生改变,则判定该类定义改变;否则判定为类定义不变,将调整过的字节码写入文件中。上述步骤1.1.2)的调整并判断域偏移是否发生改变的详细步骤是前提调整过程中如果发现填补空位导致增大对象实例大小,则停止调整。a)将旧类域进行布局,并将域按照偏移排序;b)从排序后的列表中按序取出一域f,若在新类中不存在相同定义域f’则继续取下一域;c)否则,若其能在不破坏前提的情况下将该域f’分配f同样的偏移,则将其放入布局上下文中,从步骤b)继续;d)否则,标记该域位移改变,将其放入布局上下文中从步骤b)继续。7上述步骤1.1.4)的调整并判断方法偏移是否发生改变的详细步骤是a)先分别将新旧两类实例方法在方法表中的偏移确定,得到方法表mtable与mtable';b)在旧类方法表mtable中取出一个方法m,若新类中方法m'偏移与之相同,即m.offset==m,.offset,则继续去旧类中取下一个方法;c)否则,若该旧类方法在旧类方法表中的偏移m.offset是属于新类方法表中父类继承方法范围,则标记该方法偏移变化;d)否则,若该旧类方法在旧类方法表中的偏移m.offset是属于该类定义方法范围,则将新类中方法偏移与新类方法表中该偏移处方法偏移交换,即mtable,[m,.offset]=mtable,[m.offset];mtable,[m,.offset].offset=m,.offset;mtable,[m.offset]=m,;m'.offset=m.offset;e)否则,若该旧类方法在旧类方法表中的偏移超出新类方法表长度,则扩展新类方法表大小,将新类方法表同一方法偏移Extend(mtable,);mtable,[m,.offset]=null;mtable,[m.offset]=m,;m'.offset=m.offset。上述步骤1.2)中,标记类存在类的方法的具体步骤是在类存在类中取出一方法,判断其在新版本类中是否存在同样的方法1.2.1)若不存在,则标记该方法为减少的方法;1.2.2)若存在则对方法存在方法判断进一步判断方法体是否改变的步骤为a)按序取新旧版本方法的指令insnOld,insnNew;b)若insnOld与insnNew不是同一条指令,则判定该方法体发生改变;c)若insnOlde与insnNew是同一条指令,则若其没有引用ConstantPool,则按指令格式比较该指令各域;若引用了ConstantPool,则取出ConstantPool中的内容进行比较;若比较结果不同,则判定该方法体发生改变。步骤1.3)中,标记引用结构发生改变类的具体步骤为1.3.1)取出一条指令,1.3.2)若该指令是new/anewarray/multianewarray,若其访问的目标类的实例域大小在上述步骤1.1.3)中被判定发生改变,则标记该方法为引用结构变化类的方法;1.3.3)若该指令是instanceof/checkcast,若其访问的目标类的继承结构在上述1.1.1)步骤中被判定发生变化,则标记该方法为引用结构变化类的方法;1.3.4)若该指令是getfield/putfield/getstatic/putstatic/invokevirtual/invokespecial之一,若其访问的目标方法、目标域的偏移在上述步骤1.1.2)和11.4)中被判定发生改变,则标记该方法为引用结构变化类的方法。步骤3.1)的详细过程是3.1.1)移除旧版本的类;83.1.2)修改ClassLoader的path已找到新类对应的字节码数据流;3.1.3)通过动态类加载机制加载新类,加载类之后,把旧类的VM元数据属性置换给新类,修改类型系统中类型Id,将旧类Id赋给新类;3.1.4)修改静态数据表中静态域和方法的偏移,将相应位置处数据替换成新类对应的指针;实例域和实例方法的偏移通过分析调整字节码二进制文件;3.1.5)调整之后进行初始化、实例化等后续操作。本发明在对Java程序进行动态更新时对二进制文件进行分析、重构的基础上,结合一种新的VM动态加载类机制,以减少运行时刻动态更新对机器码的重编译所带来的开销,提高更新的效率。图1显示了根据Java类发生的更新不同进行不同的更新操作。图2显示了本发明的分析工具对类层次的标记流程,图3显示了本发明的分析工具对类结构进行分析标记的流程,图4显示了本发明的分析工具对方法体进行分析标记的流程,图5显示了本发明的分析工具找出引用更新方法的流程,图6是实施例中一次Java程序更新前后不同版本程序中类的内容,图7是图6中类A、B的方法表,图8是图6中类B方法m3的字节码。具体实施例方式以下结合具体实施例对本发明作进一步描述。如图6所示为一次Java程序更新前后不同版本程序中类的内容。其中左图列出的为版本一程序中的类,右图中列出的是版本二程序中的类。在分析的阶段,采用图2所示之流程,首先将版本一中类按照继承关系进行拓扑排序,于是取出进行比较的顺序为A—B—C—D—E。按照图2之流程,取出A判断出版本二中存在同样的类A’,接着判断类A的类结构是否发生改变。按照图3之流程,A的父类更新前后都是Object类未变化,A类域未变化,对象大小也同样未变化,A类增加了方法,所以类结构发生变化。通过进一步分析比较,可以判断A类方法ml的偏移可以保持不变。将类A标记成类存在但结构变化的类,接着按照图4之流程比较A中方法实现,但由于这里没有给出A中方法的实现,关于图4之比较方法流程将在比较类C的时候阐述。按照图2之流程,比较完A之后接着取出类B进行分析,发现版本二中存在同样的类B’,接着判断类B的结构是否发生改变。按照图3之流程,B类的父类A类被被标记为类存在但结构发生改变,B受其影响必然被同样标记为类存在但结构发生改变的。按照图3之流程继续判断B改变的细节,发现B中增加了新实例域j,减少了静态域s,实例域i更新前后不变,且其偏移可以通过调整保持不变。之后,判断类实例大小,由于减少了是静态域,增加了实例域,所以B类实例大小增大。按照图3之流程继续判断类B的方法偏移情况,发现类B移除了方法m2的实现,并覆写了父类A中的方法ml,方法m3保持不变。B类中方法9的偏移均可以保持,如图7所示。接着将会按照图4之流程比较A中方法的实现,B中方法m3的字节码如图8所示。按照图4之流程,比较m3的前两条指令时通过opcode即可判断为相同指令。第三条指令均有操作数且相同,但由于该指令的操作数引用了ConstantPool中的符号,所以需要取出该符号进行比较发现不同,于是比较至此即可判断方法m3更新前后发生了改变。按照图2之流程,比较完类B之后取出类C进行分析,发现版本二中存在同样的类C’,接着判断类C的结构是否发生改变。按照图3之流程,类C的父类、域、方法均未改变,于是类C被标记为类存在且结构未变。继续比较类C的方法ml的,按照图4之流程,同样可判断ml方法发生改变,将类C中方法ml标记为受限方法。按照图2之流程,比较完类C之后取出类D进行分析,发现版本二中存在同样的类D’,接着判断类C的结构是否发生改变。分析可发现D类更新前后完全一致,但D类引用了类B。将在后续的第二次遍历中,按照图5之流程,找出需要重新编译的方法。按照图2之流程,比较完类D之后取出类E进行分析,发现版本二中并不存在类E,所以类E中所有方法均标记为受限方法。按照图2之流程,比较完类E之后第一遍遍历结束。接着进行第二次遍历来找出需要重新编译的方法。按照图5之流程,在分析D中方法m时,发现该方法引用了B中的方法ml、m2、m3的位移均未发生改变。但在分析D的初始化方法时,发现B的对象大小发生改变,所以该构造方法需要重新编译,将该初始化方法标记为可OSR的受限方法。所有分析结束后,我们需要重构类A和类B的二进制字节码文件,以保证在运行时刻加载时可以保证相应方法和域的位移不变。在运行时刻更新时,按照图1之流程加载并更新程序。A与类B需要重新加载类并生成新的VM元数据,以及编译新的方法’类C需要将方法ml重新编译;类D需要将其初始化方法重新编译’类E将其移除即可,这一点可以有JVM的垃圾回收管理机制来完成。我们的静态分析工具在分析阶段确定所有类的方法表,并通过一系列调整手段尽量保证更新前后都存在的方法位移不变,并将结果反馈到原始二进制文件中。通过我们的静态分析工具,可以有效的降低那些字节码发生改变但机器码需要重新编译的方法的数Mo权利要求一种基于字节码文件重构的Java类在线更新方法,其特征在于包含以下步骤1)分析新旧版本程序中二进制字节码文件,找出类更新、类方法体更新、类引用方法更新所针对的类;2)根据分析结果在运行时刻找寻合适的更新点;3)在运行时刻,根据类发生的更新,对其做相应的更新操作,具体为3.1)对于类更新的类,将新类替换旧类;3.2)对于类方法体更新的类,将其定义的方法重新读取并初始化可用;3.3)对于类引用方法更新的类,将存在相应引用的方法重新编译;4)将堆区中类型改变的类实例转换成新类型实例。2.根据权利要求1所述的基于字节码文件重构的Java类在线更新方法,其特征在于上述步骤1)的过程为基于JVM的更新的分析工具对所有类遍历两遍,第一次遍历(1.1)根据旧版本中类在新版中出现的情况,对旧版本类进行分类,标记出减少的类和新旧版本中都存在的类;对新旧版本中都存在的类,进一步标记出类定义变化和类定义不变化的类;(1.2)根据旧版本中类存在的类中方法在新版中出现的情况,标记出减少的方法、都存在但方法体变化的方法,以及都存在且方法体不变的方法;第二次遍历(1.3)对那些存在于新旧版本中且方法体保持不变的方法,标记出引用类定义改变的类的方法;标记输出结果(1.4)将步骤11)中减少的类、类定义变化的类中的所有方法,类定义不变的类中方法体发生改变的方法、方法体不变但引用类定义改变的类的方法标记为受限方法;(1.5)将步骤14)中标记出的受限方法中,字节码不变的方法标记为字节码不变的受限方法。3.根据权利要求2所述基于字节码文件重构的Java类在线更新方法,其特征在于上述步骤1.1)标记类的具体步骤为在旧版本中取一类C,判断其在新版本中是否存在同样的C类,若不存在则标记C为减少的类;若存在,则进一步比较类的定义是否发生改变,而判断类定义是否发生改变的步骤为(1.1.1)判断二进制文件中的类基本信息是否发生改变,若父类为减少的类则标记该类类继承关系变化;(1.1.2)判断并标记域存在域在运行时刻偏移是否能保持不变,根据相应VM平台的实例域的布局计算出实例域在堆区对象实例空间中的偏移,该偏移与域出现在二进制文件中的顺序相关,对编译器生成的二进制字节码文件进行重构,调整域出现的顺序;(1.1.3)在1.1.2)调整布局之后,必要时判断并标记出类的实例大小是否发生改变;1.1.4)判断并标记方法存在方法在运行时刻偏移是否能保持不变,根据相应VM平台实例方法偏移算法计算出偏移,该偏移与方法出现在二进制文件中的顺序有关,必要时对编译器生成的二进制字节码文件进行重构,调整方法出现的顺序;·1.1.5)若有以上之一发生改变,则判定该类定义改变;否则判定为类定义不变,将调整过的字节码写入文件中。4.根据权利要求3所述的基于字节码文件重构的Java类在线更新方法,其特征在于上述步骤1.1.2)的调整并判断域偏移是否发生改变的详细步骤是前提调整过程中如果发现填补空位导致增大对象实例大小,则停止调整,a)将旧类域进行布局,并将域按照偏移排序;b)从排序后的列表中按序取出一域f,若在新类中不存在相同定义域f’则继续取下一域;c)否则,若其能在不破坏前提的情况下将该域f’分配f同样的偏移,则将其放入布局上下文中,从步骤b)继续;d)否则,标记该域位移改变,将其放入布局上下文中从步骤b)继续。5.根据权利要求3所述的基于字节码文件重构的Java类在线更新方法,其特征在于上述步骤1.1.4)的调整并判断方法偏移是否发生改变的详细步骤是a)先分别将新旧两类实例方法在方法表中的偏移确定,得到方法表mtable与mtable';b)在旧类方法表mtable中取出一个方法m,若新类中方法m‘偏移与之相同,即m.offset==m,.offset,则继续去旧类中取下一个方法;c)否则,若该旧类方法在旧类方法表中的偏移m.offset是属于新类方法表中父类继承方法范围,则标记该方法偏移变化;d)否则,若该旧类方法在旧类方法表中的偏移m.offset是属于该类定义方法范围,则将新类中方法偏移与新类方法表中该偏移处方法偏移交换,即mtable,[m,.offset]=mtable,[m.offset];mtable,[m,.offset].offset=m,.offset;mtable,[m·offset]=m,;m'.offset=m.offset;e)否则,若该旧类方法在旧类方法表中的偏移超出新类方法表长度,则扩展新类方法表大小,将新类方法表同一方法偏移Extend(mtable,);mtable’[m’.offset]=null;mtable,[m·offset]=m,;m'.offset=m.offset。6.根据权利要求2所述的基于字节码文件重构的Java类在线更新方法,其特征在于上述步骤1.2)中,标记类存在类的方法的具体步骤是在类存在类中取出一方法,判断其在新版本类中是否存在同样的方法·1.2.1)若不存在,则标记该方法为减少的方法;·1.2.2)若存在则对方法存在方法判断进一步判断方法体是否改变的步骤为a)按序取新旧版本方法的指令insnOld,insnNew;b)若insnOld与insnNew不是同一条指令,则判定该方法体发生改变;c)若insnOlde与insnNew是同一条指令,则若其没有引用ConstantPool,则按指令格式比较该指令各域;若引用了ConstantPool,则取出ConstantPool中的内容进行比较;若比较结果不同,则判定该方法体发生改变。7.根据权利要求2所述的基于字节码文件重构的Java类在线更新方法,其特征在于步骤1.3)中,标记引用结构发生改变类的具体步骤为(1.3.1)取出一条指令,(1.3.2)若该指令是new/anewarray/multianewarray,若其访问的目标类的实例域大小在上述步骤1.1.3)中被判定发生改变,则标记该方法为引用结构变化类的方法;(1.3.3)若该指令是instanceof/checkcast,若其访问的目标类的继承结构在上述1.1.1)步骤中被判定发生变化,则标记该方法为引用结构变化类的方法;(1.3.4)若该指令是getfield/putfield/getstatic/putstatic/invokevirtual/invokespecial之一,若其访问的目标方法、目标域的偏移在上述步骤1.1.2)和11.4)中被判定发生改变,则标记该方法为引用结构变化类的方法。8.根据权利要求1中所述的基于字节码文件重构的Java类在线更新方法,其特征为步骤3.1)的详细过程是(3.1.1)移除旧版本的类;(3.1.2)修改ClassLoader的path已找到新类对应的字节码数据流;(3.1.3)通过动态类加载机制加载新类,加载类之后,把旧类的VM元数据属性置换给新类,修改类型系统中类型Id,将旧类Id赋给新类;(3.1.4)修改静态数据表中静态域和方法的偏移,将相应位置处数据替换成新类对应的指针;实例域和实例方法的偏移通过分析调整字节码二进制文件保持不变;(3.1.5)调整之后进行类的初始化、实例化等后续操作。全文摘要本发明公开了一种基于字节码文件重构的Java类在线更新方法,该方法在对Java程序进行动态更新时对二进制文件进行分析、重构,结合一种新的VM动态加载类机制,以减少运行时刻动态更新对机器码的重编译所带来的开销,提高更新的效率。文档编号G06F9/445GK101963914SQ20101054410公开日2011年2月2日申请日期2010年11月12日优先权日2010年11月12日发明者吕建,曹春,马晓星申请人:南京大学
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1