CN101493767B - 一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法 - Google Patents

一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法 Download PDF

Info

Publication number
CN101493767B
CN101493767B CN2009100772519A CN200910077251A CN101493767B CN 101493767 B CN101493767 B CN 101493767B CN 2009100772519 A CN2009100772519 A CN 2009100772519A CN 200910077251 A CN200910077251 A CN 200910077251A CN 101493767 B CN101493767 B CN 101493767B
Authority
CN
China
Prior art keywords
pitching pile
dead
dead object
information
quoting
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Expired - Fee Related
Application number
CN2009100772519A
Other languages
English (en)
Other versions
CN101493767A (zh
Inventor
张昱
袁丽娜
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
University of Science and Technology of China USTC
Original Assignee
University of Science and Technology of China USTC
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by University of Science and Technology of China USTC filed Critical University of Science and Technology of China USTC
Priority to CN2009100772519A priority Critical patent/CN101493767B/zh
Publication of CN101493767A publication Critical patent/CN101493767A/zh
Application granted granted Critical
Publication of CN101493767B publication Critical patent/CN101493767B/zh
Expired - Fee Related legal-status Critical Current
Anticipated expiration legal-status Critical

Links

Images

Abstract

本发明公开了一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法,技术方案包括:将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中;所述插桩信息包含为死亡对象选择的引用及确定的插桩位置;根据所述插桩信息,创建对应的显式内存释放指令,将所述显式内存释放指令安插到对应的位置上,以对所述死亡对象的内存空间进行释放。采用本发明所述的技术方案,不仅能够准确有效的将显式释放内存指令插入合理的位置而不导致编译和运行时的错误,并且可以将死亡对象空间更多的被释放。

Description

一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法 
技术领域
本发明涉及即时编译器辅助的垃圾收集技术领域,具体涉及一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法。 
背景技术
Java语言采用GC(Garbage Collector,垃圾收集器)这种自动的内存管理技术在堆上管理对象的分配与回收。当堆空间不足时,GC就会自动启动垃圾收集来识别并回收死亡对象所占用的内存空间,即由GC隐式地回收死亡对象。这种内存管理方式减轻了程序员的编程负担,降低了程序中与内存相关错误的发生几率,然而它也为整个系统运行带来了额外的时空开销,是影响Java虚拟机性能的重要因素之一。 
C与C++这类语言采用显式的内存管理技术,由程序员在编程时插入显式的释放内存的指令来回收内存空间。这种显式的内存管理可以在很小的代价下快速回收内存空间;但却给程序员带来很大的编程负担从而影响编程效率,并且容易引起与内存相关的错误,如内存泄漏、悬空引用等。 
GC在进行对象回收时,有大量的时空开销消耗在识别堆中哪些对象是死亡的。为了提高GC管理内存的性能,目前一种新的解决方式是JIT(Just-in-time Complier,即时编译器)辅助的垃圾收集(JIT-assisted-GC,Just-in-time compiler assisted garbage collection)技术。JIT-assisted-GC结合了显式和自动(又称隐式)内存管理的优点,是在编译阶段由JIT分析应用程序以识别确定其中的死亡对象及位置,并根据死亡对象的位置信息在相应的位 置上安插显式释放内存的指令来主动通知GC回收内存,GC接到显式内存释放请求后,不仅可以立即回收内存空间还可以采取某些策略对这些空间进行有效重用,从而减轻了GC自动回收内存的负担。 
现有的JIT-assisted-GC中的显式释放对象的插桩方法主要有两种。一是通过过程内的别名分析和全局约束系统,分析出程序中具有唯一引用(变量或对象的域)的对象的生命期,在这些具有唯一引用的对象的死亡位置安插显式内存释放指令;二是利用流不敏感的指针分析和流敏感的活跃变量分析,能够分析出程序中的短生命期对象,并在短生命期对象的死亡位置安插显式内存回收指令,这种方法无法识别被对象的域所引用的对象的生命期。 
在实施本发明的过程中,发明人发现现有技术至少存在以下问题: 
1)现有技术识别出死亡对象时,直接在死亡对象的死亡位置上安插显式释放内存的指令来主动通知GC回收内存,但对于在分支中创建的对象若其生命期在分支外结束,此时直接在死亡位置安插显式内存释放指令会导致:当程序运行经过其他未创建该对象的分支后,执行所安插的显式内存释放指令时,将导致释放了一个不存在的死亡对象(即悬空引用);例如:假设程序执行的控制流中的某个位置存在分支A和分支B,这两条分支随后又聚合为一条分支C,对象a在分支A内创建,但是对象生命期分析识别出对象a在分支C的某个位置死亡,如果将显式内存释放指令安插在分支C处,就会导致当程序执行实际走的是分支B时,就会在执行到分支C时释放了一个不存在的死亡对象,因为对象a并没有在分支B中创建; 
2)当死亡对象在Java程序的方法(也称函数)中存在多个引用时,现有技术不能提供一个很好的选择策略来释放死亡对象所占用的内存空间; 
3)当死亡对象在Java程序的方法(也称函数)中没有显式的出现引用时,现有技术无法释放其内存空间。 
发明内容
鉴于上述现有技术所存在的问题,本发明的目的是提供一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法,不仅能够准确有效的将显式释放内存指令插入合理的位置而不导致编译的错误,并且可以将死亡对象空间更多的被释放。 
本发明的目的是通过以下技术方案实现的: 
本发明提供了一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法,包括: 
将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中;所述插桩信息包含为死亡对象选择的引用及确定的插桩位置; 
根据所述插桩信息,创建对应的显式内存释放指令,将所述显式内存释放指令安插到对应的位置上,以对所述死亡对象的内存空间进行释放; 
所述死亡对象包括:直接死亡对象和与所述直接死亡对象有域引用关系的间接死亡对象;所述死亡对象信息包括:直接死亡对象及对应的死亡位置和所述直接死亡对象的域信息,以及与所述直接死亡对象有域引用关系的间接死亡对象及对应的死亡位置; 
将所述显式内存释放指令安插到对应的位置上的具体过程包括: 
判断所述插桩信息中所述直接死亡对象或与所述直接死亡对象有域引用关系的所述间接死亡对象的插桩位置是否为分支跳转指令,当为分支跳转指令时,将所述显式内存释放指令安插在所述分支跳转指令的每个跳转目标的入口处; 
否则,进一步判断所述直接死亡对象或所述间接死亡对象确定的插桩位置,当所述插桩位置为返回语句所在的结点时,则在所述插桩位置的最后一条指令之前安插所述显式内存释放指令;否则,在所述插桩位置的指令的最后一条指令之后安插所述显式内存释放指令。 
进一步的,将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中的具体过程包括: 
为所述直接死亡对象选择引用及确定插桩位置; 
对于与所述直接死亡对象有域引用关系的所述间接死亡对象,根据为所述直接死亡对象选择的引用、所述域信息和域引用关系,确定所述间接死亡对象的引用及其插桩位置; 
将所述直接死亡对象的引用及插桩位置以及所述间接死亡对象的引用及插桩位置,根据所述插桩类型条件进行判断,记录满足所述条件的引用及插桩位置,以获得所述插桩信息。 
进一步的,所述插桩类型条件包括:在程序执行的控制流上,为所述死亡对象选择的引用所在的结点要支配所述死亡对象的插桩位置所在的结点。 
进一步的,为所述直接死亡对象选择的引用包括:对象分配点处的引用和由phi语句、赋值语句或域赋值语句带来的引用中的至少一种; 
当为所述直接死亡对象选择引用时,所述对象分配点处的引用的优先级大于所述由phi语句、赋值语句或域赋值语句带来的引用的优先级。 
进一步的,根据所述插桩信息,创建相应的显式内存释放指令的具体过程包括: 
对所述插桩信息中为所述死亡对象选择的引用进行判断; 
当所述直接死亡对象的引用是所述对象分配点处的引用或是由phi语句或赋值语句带来的引用时,直接根据所述直接死亡对象的引用创建相应的显式内存释放指令,以对所述直接死亡对象的内存空间进行释放;或者, 
当所述直接死亡对象的引用为所述域赋值语句带来的引用,则首先创建取域地址指令来获得存放所述直接死亡对象的引用的域的地址,然后根据所述域的地址新建加载指令来获得所述直接死亡对象的位置信息,最后根据所述位置信息创建显式内存释放指令,以对所述直接死亡对象的内存空间进行释放。 
进一步的,所述方法还包括: 
对于所述间接死亡对象的引用,首先创建取域地址指令来获得存放所述间接死亡对象的引用的域的地址,然后根据所述域的地址新建加载指令来获得所述间接死亡对象的位置信息,最后根据所述位置信息创建显式内存释放指令,以对与所述直接死亡对象有域引用关系的所述间接死亡对象的内存空间进行释放。 
进一步的,所述死亡对象插桩位置为:Java程序中的方法的返回语句所 在的结点和经过对象生命期分析后获得的对象死亡的位置中的至少一种; 
当为所述死亡对象确定插桩位置时,所述返回语句所在的结点的优先级大于所述经过对象生命期分析后获得的对象死亡的位置的优先级。 
本发明还提供了一种即时编译器辅助的垃圾收集中显式释放对象的插桩装置,所述装置包括: 
插桩信息收集模块,用于将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中;所述插桩信息包含为死亡对象选择的引用及确定的插桩位置;所述死亡对象包括:直接死亡对象和与所述直接死亡对象有域引用关系的间接死亡对象;所述死亡对象信息包括:直接死亡对象及对应的死亡位置和所述直接死亡对象的域信息,以及与所述直接死亡对象有域引用关系的间接死亡对象及对应的死亡位置; 
插桩信息执行模块,用于根据所述插桩信息,创建对应的显式内存释放指令,将所述显式内存释放指令安插到对应的位置上,以对所述死亡对象的内存空间进行释放;所述插桩信息执行模块将所述显式内存释放指令安插到对应的位置上的具体过程包括: 
判断所述插桩信息中所述直接死亡对象或与所述直接死亡对象有域引用关系的所述间接死亡对象的插桩位置是否为分支跳转指令,当为分支跳转指令时,将所述显式内存释放指令安插在所述分支跳转指令的每个跳转目标的入口处; 
否则,进一步判断所述直接死亡对象或所述间接死亡对象确定的插桩位置,当所述插桩位置为返回语句所在的结点时,则在所述插桩位置的最后一条指令之前安插所述显式内存释放指令;否则,在所述插桩位置的指令的最后一条指令之后安插所述显式内存释放指令。 
本发明有益效果: 
由上述本发明提供的技术方案可以看出,本发明所述的一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法,主要有以下三个优点: 
1、本发明的插桩方法考虑了控制流中的支配关系,保证对象的分配点所在基本块结点支配显式释放对象指令插桩所在的结点,从而确保被插桩的显式释放内存指令的正确性; 
2、在描述对象引用时引入域引用关系描述,并且在插桩时能够主动生成指令获得对象的域引用,从而释放对象的域所引用的对象内存空间,达到对整个对象空间的释放。 
3、为不同对象提供不同的插桩类型,保证可以高效地利用对象生命期分析信息,使得分析出的死亡对象空间尽可能多的被释放。 
附图说明
图1本发明实施例一所述方法的简要流程图; 
图2本发明实施例一中方法M的控制流程图; 
图3为本发明应用实例中即时编译器辅助的垃圾收集框架图; 
图4为本发明实施例一中Jolden中基准程序BH的代码片段; 
图5为本发明实施例一中BH的性能对比图; 
图6为本发明实施例一中TSP的性能对比图; 
图7为本发明实施例一中Power的性能对比图; 
图8为本发明实施例一中Health的性能对比图。 
图9为本发明实施例二中所述装置的简化结构示意图。 
具体实施方式
本发明所述技术方案提出了一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法,结合了显式和自动(又称隐式)内存管理的优点,在编译阶段由JIT(即时编译器)分析应用程序以识别确定其中的死亡对象及位置, 并在程序中合适的位置安插显式释放内存的指令来主动通知GC(垃圾收集器)回收内存,解决了现有技术中因为某些情况下直接在对象死亡位置安插显式的内存释放指令会引起编译时或运行时的错误; 
例如:在分支中创建的对象若其生命期在分支外结束,此时直接在死亡位置安插显式内存释放指令就会导致运行时错误。在GC接到显式内存释放请求后,不仅可以立即回收内存空间还可以采取某些策略对这些空间进行有效重用,从而减轻了GC自动回收内存的负担。 
本发明还解决了如何释放那些在Java程序的方法M外创建,在M内死亡,但在M中没有显式引用的对象空间。例如:某些通过M中的调用点处的实参和返回值接收者的域带回到M的对象。本发明所述技术方案更好地利用程序分析结果,使得分析出的死亡对象的内存空间能够尽可能多地被释放。 
为了更好的理解本发明所述的技术方案,下面结合具体实施方式来进行说明。 
实施例一: 
本实施例中假定程序基于静态单赋值(Static Single Assignment,SSA)格式,采用SSA格式可以使得程序分析具有流敏感性;其次,假定所有形如x.y.f这类多级的变量表示方式将转化为t=x.y和t.f。 
为了更好的说明本发明中涉及的技术方案,下面引入几个定义。 
定义1.对于方法M的控制流图中的两个基本块结点A和B,如果到达结点B的控制流在到达B之前必经过结点A,称结点A支配结点B,记作Dominate(A,B)=true。 
必须保证对象的分配点所在基本块结点支配显式释放对象指令插桩所在的结点,即在释放对象空间时必须保证无论程序沿着哪条路径执行时均会先创建对象,否则程序执行时会发生错误。通过为方法M计算支配树,可以得 出M的控制流图上各个结点之间的支配关系。 
定义2.对于方法M,M中死亡对象的插桩信息是一个二元组集合,即Instrumentlnfo={<ref,loc>}。其中ref描述某死亡对象O的引用,它是一个二元组即ref=<var,fielddesc>,若fielddesc为NULL,说明变量var是O的引用;若fielddesc为某个域f,说明var.f是O的引用,即此时的死亡对象O被变量var所引用的对象的f域所引用。loc描述插桩位置,在此代表控制流图中的结点。 
对于那些在方法M中创建并在M中死亡的对象,在M中肯定会显式出现引用它的变量。那些在M外创建但在M中死亡的对象,在M中则不一定会显式出现引用它的变量。此时需要借助其它变量来获得对这些对象的引用,这些对象的生命期在M中结束说明它们一定曾被M中出现的变量的域所引用,因此可以用这些变量和域描述一起间接地表示这些对象的引用。 
如图1所示,本发明所述一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法,所述方法包括: 
步骤S101:将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中;所述插桩信息包含为死亡对象选择的引用及确定的插桩位置; 
步骤S102:根据所述插桩信息,创建对应的显式内存释放指令,将所述显式内存释放指令安插到对应的位置上,以对所述死亡对象的内存空间进行释放。 
具体地说,所述死亡对象包括:直接死亡对象和与所述直接死亡对象有域引用关系的间接死亡对象; 
所述死亡对象信息包括:直接死亡对象及对应的死亡位置和所述直接死亡对象的域信息,以及与所述直接死亡对象有域引用关系的间接死亡对象及对应的死亡位置。 
所述域信息表示所述死亡对象中包含的数据成员,通常也可以称为数据成员。 
直接死亡对象是通过对象生命期分析后获得能够直接确认死亡的对象; 
间接死亡对象是通过对象生命期分析后根据与直接死亡对象的域引用关系获得的。 
具体地说,将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中的具体过程包括: 
为所述直接死亡对象选择引用及确定插桩位置; 
对于与所述直接死亡对象有域引用关系的所述间接死亡对象,根据为所述直接死亡对象选择的引用、所述域信息和域引用关系,确定所述间接死亡对象的引用及其插桩位置; 
将所述直接死亡对象的引用及插桩位置以及所述间接死亡对象的引用及插桩位置,根据所述插桩类型条件进行判断,记录满足所述条件的引用及插桩位置,以获得所述插桩信息。 
说明:在Java程序中,对象是通过对象的引用变量(reference variable)来访问的,该变量包含对象的引用地址,该引用变量简称为引用。一个对象可以为程序中的多个变量所引用,一个变量在程序的不同运行时刻会引用多个不同的对象。 
例如对于一条新建对象语句: 
SubClass mySubClass=new SubClass(....); 
将创建一个类型为SubClass的对象,并将它的引用地址赋值给引用变量mySubClass,即变量mySubClass为该新创建的对象的引用. 
具体地说,为直接死亡对象选择的引用可以包括:对象分配点处的引用和由phi语句、赋值语句或域赋值语句带来的引用中的至少一种; 
当为所述直接死亡对象选择引用时,所述对象分配点处的引用的优先级大于所述由phi语句、赋值语句或域赋值语句带来的引用的优先级。 
对对象分配点处的引用进行说明: 
一个对象的分配点是指创建该对象的new表达式所在的指令位置。 
例如:a=new A(),则称“new A()”是一个对象分配点,它创建了一个类型为A的对象,并将这个对象的引用赋值给变量a。 
例如:a=new A(),a为该分配点所创建的对象的引用。特别地,对于v=v0.foo(v1,v2,...,vn);若foo方法返回一个新创建的对象O1,此时v就为对象O1的引用;若foo方法通过v或vi(i=0,1,...,n)的某个域f返回一个新创建的对象Of,此时v.f或vi.f(i=0,1,...,n)就为对象Of的引用。我们称该类引用是第I类引用,每个对象只有一个该类引用,记为引用refl(O)。 
对由phi语句、赋值语句或域赋值带来的引用进行说明: 
例如: 
if(...){ 
  a1=new A(); 
  …… 
}else{ 
  a2=new A(); 
  …… 
a=phi(a1,a2), 
在上述程序片段中,if语句的两个分支分别为变量a1和a2创建了对象,当执行“a=phi(a1,a2)”时,利用phi语句将变量a1和a2统一为变量a,即让a表示为a1或a2所引用的对象的引用;a=b,则a为b所引用的对象的引用;a.f=b,则a.f为b所引用的对象的引用。我们称该类引用是第II类引用,每个对象的第II类引用的个数不定,一般大于等于1,记为引用集合Refll(O)。 
具体地说,所述死亡对象插桩位置包括:Java程序中的方法的返回语句所在的结点和经过对象生命期分析后获得的对象死亡的位置中的至少一种; 
当为所述死亡对象确定插桩位置时,所述返回语句所在的结点的优先级大于所述经过对象生命期分析后获得的对象死亡的位置的优先级。 
对return(返回)语句所在的结点进行说明:在此简称return结点。在程序不发生异常情况下,肯定有一个return结点会被执行到,所以这是一个可供选择的位置。称该种插桩位置为第I类插桩位置,该类结点是一个集合,个数至少为1,与对象无关,记为位置集合Locl(O)。 
对对象生命期分析得到的对象死亡位置进行说明:因为我们分析的粒度是精确到基本块的,所以这个位置是可以获得的。我们称该种插桩位置为第II类插桩位置,每个死亡对象只有一个第II类插桩位置,记为位置locll(O)。 
说明:所述死亡信息中包含死亡对象及其死亡位置,对一个死亡对象O,用refl(O)=<varl,fielddescl>和Refll(O)={<varll,fielddescll>}分别表示对象O的第I类引用和第II类引用集合,Locl(O)和locll(O)分别表示对象O的第I类插桩位置集合和第II类插桩位置,Node(var)表示变量var第一次出现时所在的基本块结点,称作var所在的结点。 
插桩信息收集算法Collector依次扫描死亡对象信息中的每个死亡对象O,当对象O满足某个插桩类型的条件时,将相应的引用和插桩位置记录到插桩信息Instrumentlnfo中。该算法总是优先地为死亡对象选择第I类引用和第I类插桩位置,插桩信息收集时的优先级为:插桩类型1>插桩类型2>插桩类型3>插桩类型4;所述插桩类型1:为死亡对象选择第I类引用和第I类插桩位置;所述插桩类型2:为死亡对象选择第I类引用和第II类插桩位置;所述插桩类型3:为死亡对象选择第II类引用和第I类插桩位置;所述插桩类型4:为 死亡对象选择第II类引用和第II类插桩位置。 
下面为本实施例中插桩信息收集算法Collector:根据方法M中的死亡对象信息,为M中的死亡对象收集插桩信息,同时在所述算法中也示出了四种插桩类型的形式。 
输入:方法M中的死亡对象信息Deadlnfo。 
输出:方法M中死亡对象的插桩信息Instrumentlnfo。 
1:for(each dead object O in Deadlnfo){ 
2:  bool isRecord=false; 
3:  obtain refl(O)=<varl,fielddescl>from Deadlnfo 
4:  for(each locl in Locl(O)){//插桩类型1 
5:    if(Dominate(Node(varl),locl)){ 
6:       record<refl(O),locl>to Instrumentlnfo; 
7:       isRecord=true; 
8:    } 
9:  } 
10: if(isRecord)continue; 
11: if(Dominate(Node(varl),locll(O)){//插桩类型2 
12:    record<refl(O),locll(O)>to Instrumentlnfo 
13:    isRecord=true; 
14: } 
15: if(isRecord)continue; 
16: obtain Refll(O)from Deadlnfo and select the last element 
refll(O)=<varll,fielddescll>from Refll(O) 
17: for(each locl in Locl(O)){//插桩类型3 
18:   if(Dominate(Node(varll),locl)){ 
19:     record<refll(O),locl>to Instrumentlnfo 
20:     isRecord=true; 
21:   } 
22: } 
23: if(isRecord)continue; 
24: if(Dominate(Node(varll),locll(O))){//插桩类型4 
25:    record<refll(O),locll(O)>to Instrumentlnfo 
26: } 
27:}//end for 
具体地说,所述插桩类型条件包括:在程序执行的控制流上,为所述死亡对象选择的引用所在的结点要支配所述死亡对象的插桩位置所在的结点。 
具体地说,为所述死亡对象选择的所述引用所在的节点为基本块节点。一个基本块包含程序中一段顺序执行的指令,这些指令的执行只有一个程序执行入口和一个程序执行出口,其间不能有其他的程序执行入口或程序执行出口。 
为了说明所述插桩类型条件的重要性,下面通过举例来说明考虑结点的控制流支配关系的必要性和各种插桩类型的适用情况。 
如图2所示,为方法M的控制流程图,在基本块结点B2和B4中分别创建了一个C类型的对象,它们均在基本块B5中死亡。Dominate(B2,B5)=Dominate(B4,B5)=false,如果不考虑支配关系,就会在B5中安插两条带有下划线的显式释放指令,而程序在执行时只会经过其中一条分支,在执行“free(c1);free(c3);”时肯定会发生错误,因为此时释放的是一个并没有创建的对象空间,c1或c3是无效的对象引用。 
一个死亡对象肯定有第I类引用,但当对象的第I类引用所在的结点不能支配return结点和对象死亡点时,此时需要使用第I I类引用。由于B5中的phi语句,所以c是c1和c3这两个变量引用的对象的第II类引用,因此可以在return结点即基本块B5中安插free(c)。 
优先地将free指令安插到return结点,这样可以保证free指令尽可能多地被执行到。基本块B1中创建了一个引用为b的B类型对象,在B2尾部死亡,若在B2中安插显式释放指令,当程序实际执行时走B1-B3-B4路径时,此时B2中的free指令就不会被执行到,而只要程序不发生异常情况,B5中的free指令一定会被执行到。对于作用域是某个语句块中的对象,如分支或循环,一般情况下均不能在return结点中安插free指令,此时对象的死亡点是一个很好的选择,如B3中创建的C类型的对象,特别的当B3处在循环中时,在对象 死亡点插桩会带来很大的收益。 
具体地说,根据所述插桩信息创建相应的显式内存释放指令的具体过程包括: 
对所述插桩信息中为所述死亡对象选择的引用进行判断; 
当所述直接死亡对象的引用是所述对象分配点处的引用或是由phi语句或赋值语句带来的引用时,直接根据所述直接死亡对象的引用创建相应的显式内存释放指令,以对所述直接死亡对象的内存空间进行释放;或者, 
当所述直接死亡对象的引用为所述域赋值语句带来的引用,则首先创建取域地址指令来获得存放所述直接死亡对象的引用的域的地址,然后根据所述域的地址新建加载指令来获得所述直接死亡对象的位置信息,最后根据所述位置信息创建显式内存释放指令,以对所述直接死亡对象的内存空间进行释放。 
进一步地说,对于所述间接死亡对象的引用,首先创建取域地址指令来获得存放所述间接死亡对象的引用的域的地址,然后根据所述域的地址新建加载指令来获得所述间接死亡对象的位置信息,最后根据所述位置信息创建显式内存释放指令,以对与所述直接死亡对象有域引用关系的所述间接死亡对象的内存空间进行释放。 
具体地说,将所述显式内存释放指令安插到对应的位置上的具体过程包括: 
判断所述插桩信息中所述直接死亡对象或与所述直接死亡对象有域引用关系的所述间接死亡对象的插桩位置是否为分支跳转指令,当为分支跳转指令时,将所述显式内存释放指令安插在所述分之跳转指令的每个跳转目标的入口处; 
否则,进一步判断所述直接死亡对象或所述间接死亡对象确定的插桩位置,当所述插桩位置为返回语句所在的结点时,则在所述插桩位置的最后一条指令之前安插所述显式内存释放指令;否则,在所述插桩位置的指令的最后一条指令之后安插所述显式内存释放指令。 
下面为本实施例中插桩算法Instrumentor:根据方法M的插桩信息,为M安插显式的内存释放指令。 
输入:方法M的HIR;在M中死亡的对象的插桩信息Instrumentlnfo。 
输出:方法M修改后的HIR。 
1:for(<ref,loc>∈Instrumentlnfo){ 
2:  x=ref.var; 
3:  if(ref.fielddesc!=NULL){ 
4:    //创建取域地址指令,fieldaddr为域地址 
    ldFieldAddrlnst=makeLdFieldAddr(x,fielddesc,fieldaddr); 
5:    ldlndlnst=makeLdlnd(fieldaddr,y); 
6:    freelnst=makeFreelnst(y); 
7:  } 
8:  else 
9:    freelnst=makeFreelnst(x); 
10: lastlnst=getLastlnst(loc); 
11: if(!isBranchJumplnst(lastlnst)){ 
12:   if(isReturnlnst(lastlnst){ 
13:      if(ref.fielddesc!=NULL){ 
14:        insertBefore(ldFieldAddrlnst,lastlnst); 
15:        insertBefore(ldlndlnst,lastlnst); 
16:      } 
17:      insertBefore(freelnst,lastlnst); 
18:   } 
19:   else{ 
20:     if(ref.fielddesc!=NULL){ 
21:       insertAfter(ldFieldAddrlnst,lastlnst); 
22:       insertAfter(ldlndlnst,lastlnst); 
23:   } 
24:   insertAfter(freelnst,lastlnst); 
25: }//end if(isReturnlnst(lastlnst) 
26:  } 
27:  else{ 
28:    for(targetlnsti∈jumpTarget(lastlnst)){ 
29:      if(ref.fielddesc!=NULL){ 
30:        insertBefore(ldFieldAddrlnst,targetlnsti); 
31:        insertBefore(ldlndlnst,targetlnsti); 
32:      } 
33:      insertBefore(freelnst,targetlnsti); 
34:    }//end for 
35:  } 
36:}//end for 
说明:当收集好插桩信息后,此时可以进行插桩,插桩算法Instrumentor首先依次扫描每条插桩信息,然后创建相应的显式内存释放指令freelnst,最后根据插桩位置在方法的控制流图中安插新创建的指令。 
特别地,如果一条插桩信息<ref,loc>中的ref.fielddesc不为空,表明此时要释放的是域引用的对象,首先需要新建一条取域地址指令获得域地址fieldaddr,然后新建一条加载指令获得域引用y,最后创建一条显式内存释放指令用于释放变量y所引用的对象空间,如Instrumentor算法的4~6行所示。插桩位置loc是精确到某个基本块的,loc的最后一条指令假设为lastlnst。如果lastlnst不是分支跳转指令,分为两种情况:如果lastlnst是return指令,则在lastlnst之前安插,如12~18行所示;否则在lastlnst之后安插显式内存释放指令,如19~25行所示。但当lastlnst为分支跳转指令时,此时在lastlnst各个跳转目标指令之前插桩,如27~35行所示。 
实施例二: 
如图9所示,本发明一种即时编译器辅助的垃圾收集中显式释放对象的插桩装置,所述装置包括: 
插桩信息收集模块S11,用于将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中;所述插桩信息包含为 死亡对象选择的引用及确定的插桩位置; 
插桩信息执行模块S12,用于根据所述插桩信息,创建对应的显式内存释放指令,将所述显式内存释放指令安插到对应的位置上,以对所述死亡对象的内存空间进行释放。 
鉴于实施例二与实施例一的技术方案相似,所以不在此赘述,具体内容请参见实施例一。 
下面结合具体实例对本发明所述的技术方案作详细说明。 
应用实例一: 
本发明所述技术方案,在开源的Java SE平台Apache Harmony[4]上,用C++实现了应用于JIT-assisted-GC插桩方法,该插桩方法可以同样地应用在其他Java虚拟机上。Jit-assisted-GC的框架如图3所示,涉及到即时编译器(JIT)、垃圾收集器(GC)、虚拟机核心(VMCore)三个模块。 
图4是Jolden[10]中基准程序BH的代码片段,这里使用Java源代码来示意,本发明的分析工作是在对应的HIR指令上展开的。 
下面以图4为例说明插桩的过程。 
在图4中,方法subdivp中有3处调用点,分别位于第14~16行。其中第14行是一个对象分配点也是隐含的调用点,它创建了一个类型为MathVector的对象,通过自动调用类MathVector的构造器来初始化新创建的对象,最后将该新创建的对象赋值给变量dr,记该新创建的对象记为Odr。在对象初始化中,构造器会创建一个double类型的数组对象并将其赋值给对象Odr的data域,记该数组对象为Odr.data。方法subtraticon只做简单的数学计算,没有改变实参和返回值。方法dotProduct也只做简单的数学计算,它的返回值是基本数据类型而非对象类型。由上可知,在方法subdivp中有两个对象是可能死亡的,即MayDead(subdivp)={Odr,Odr.data},而这两个对象不是全局对象,即不会被subdivp所在线程之外的线程所访问,其生命期在subdivp中 结束,因此可以在它们死亡之后将其内存空间显式释放。 
假设在方法subdivp的HIR中,将图中第14行语句所在的基本块结点记为NAllocate,第16行语句所在的基本块结点记为NDead,则对象Odr和Odr.data的分配点指令所在基本块结点都为NAllocate,它们的死亡位置都为NDead。 
插桩信息收集算法Collector处理过程: 
对于对象Odr.data,,由于在方法subdivp中没有显式出现它的引用,需借助其他对象的域引用来表示,引用ref 1=<dr,data>。由于Dominate(NAllocate,return)=true,所以为对象Odr.data选择第一种插桩类型,插桩信息为<ref 1,return>。 
对象Odr在方法subdivp中创建,引用ref 2=<dr,NULL>。由于Dominate(NAllocate,return)=true,所以为对象Odr选择第一种插桩类型,插桩信息为<ref 2,return>。 
插桩信息收集算法Collector为方法subdivp收集到的插桩信息为{<ref1,return>,<ref 2,return> 
插桩算法Instrumentor处理过程: 
对于插桩信息<ref1,return>,由于ref1.field!=NULL,所以即时编译器首先新建一条取域地址指令获得域地址fieldaddr,然后新建一条加载指令获得域引用v(源代码对应于图中的第17行,实际在HIR中生成两条指令);最后创建一条显式内存释放指令free(v)用于释放变量v所引用的对象空间(如18行所示)。由于插桩位置是return结点,所以在return结点的最后一条return指令之前插桩上面创建的指令,如17-18行所示。 
对于插桩信息<ref2,return>,由于ref2.field==NULL,所以即时编译器直接创建一条显式内存释放指令free(ref2.var)=free(dr)用于释放变量dr所引用的对象空间(如19行所示)。由于插桩位置是return结点,所以在return结 点的最后一条return指令之前插桩上面创建的指令,如第19行所示。 
当程序在实际执行中遇到显式释放内存指令free时,VMCore会去启动GC,调用GC提供的Free(obj)接口来真正回收内存空间,而不用再消耗时空去识别该对象是否是死亡的,同时GC还可以改进它的分配接口Allocate(obj),使得显式回收的空间能够尽可能地被重用,从而减轻了GC自动回收内存的负担并提高内存空间的利用率。 
本发明所述的技术方案通过正确性验证,检验了对象生命期分析的正确性和插桩的正确性。经过体现在是否提前释放还没有死亡的对象空间或是在错误的位置释放死亡的对象空间。为了进行该项测试,修改了后端GC侧的free()接口,将回收的内存空间标记为不可访问和不可重用的。这样如果错误地释放了某个对象空间,则再次访问该对象时会发生异常。另外在流水线上对LIR有多项检测,如检验操作数在方法入口处是否活跃,辅助地测试了插桩的正确性,例如图2中若在B5中插桩那两条带有下划线的free指令,则操作数c1和c3在程序入口处就是活跃的,说明代码肯定有错误。实验结果显示程序没有发生编译时错误、运行时异常或错误,说明所设计和实现的算法能正确地释放死亡对象的内存空间。 
本发明所述的技术方案在虚拟机默认的堆大小(256MB)下,测试了JIT-assisted-GC显式回收对象空间大小和各种插桩类型的收益,结果如表1和表2所示。 
表1插桩算法回收的内存空间大小 
Figure G2009100772519D00191
Figure G2009100772519D00201
从表1可以看出,4个基准程序的大部分内存空间都可以被显式回收。最少的Health释放了23%的对象空间,最多的Power分配了24MB的内存,显式释放了23MB的内存空间。表1的第3列表示插桩的显式内存回收指令数,那些在循环中或者是递归调用方法中插桩的显式释放指令带来的收益最多,例如Health回收的14MB空间中将近14MB都来自于其中,这些地方插桩的显式释放指令回收的空间也最容易被重用。 
表2各种插桩类型的收益 
Figure G2009100772519D00202
表2所示的是各种插桩类型下的收益。插桩类型1安插的显式内存回收指令只要在程序不发生异常的情况下,总会被执行到,所以这种插桩类型的收益远远大于其它3种。插桩类型3的对象很少,因为如果一个对象的第I类引用所在的结点不能够支配return结点,则它的第II类引用所在的结点也很难支配return结点。插桩类型2和插桩类型4针对的均是作用域在某个语句块的对象,如在一个分支中创建且在该分支中死亡的对象。 
为比较原有的GC和JIT-assisted-GC在性能上的差异,在不同堆大小情 况下对运行四个基准程序时GC的停顿时间进行考核,结果见图5至8所示,其中横轴表示GC的堆大小,纵轴表示GC的停顿时间。 
从图5至8可以看出,对于这四个基准程序,JIT-assisted-GC在停顿时间上都少于原有的GC,提高了应用程序的执行效率。随着堆的大小变大,性能提升的幅度会逐渐变小,这是因为可以显式回收的对象数目固定,而当堆变大时,同样大小的重用空间带来的性能提升相对减少,如图6和8所示。而在图5和图7中,即使将堆大小设置为最小的16MB,JIT-assisted-GC的停顿时间都为0,这是因为此时使用JIT-assisted-GC恰好不会发生垃圾收集过程。 
经过实验论证,本发明所述的一种即时编译器赋值的垃圾收集中显式释放对象的插桩方法,根据控制流中的支配关系,确保了被插桩的显式释放内存指令的正确性;在描述对象引用时引入域引用关系描述,并且在插桩时能够主动生成指令获得对象的域引用,从而释放对象的域所引用的对象内存空间,达到对整个对象空间的释放。 
以上所述,仅为本发明较佳的具体实施方式,但本发明的保护范围并不局限于此,任何熟悉本技术领域的技术人员在本发明揭露的技术范围内,可轻易想到的变化或替换,都应涵盖在本发明的保护范围之内。因此,本发明的保护范围应该以权利要求的保护范围为准。 

Claims (8)

1.一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法,其特征在于,包括:
将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中;所述插桩信息包含为死亡对象选择的引用及确定的插桩位置;
根据所述插桩信息,创建对应的显式内存释放指令,将所述显式内存释放指令安插到对应的位置上,以对所述死亡对象的内存空间进行释放;
所述死亡对象包括:直接死亡对象和与所述直接死亡对象有域引用关系的间接死亡对象;所述死亡对象信息包括:直接死亡对象及对应的死亡位置和所述直接死亡对象的域信息,以及与所述直接死亡对象有域引用关系的间接死亡对象及对应的死亡位置;
将所述显式内存释放指令安插到对应的位置上的具体过程包括:
判断所述插桩信息中所述直接死亡对象或与所述直接死亡对象有域引用关系的所述间接死亡对象的插桩位置是否为分支跳转指令,当为分支跳转指令时,将所述显式内存释放指令安插在所述分支跳转指令的每个跳转目标的入口处;
否则,进一步判断所述直接死亡对象或所述间接死亡对象确定的插桩位置,当所述插桩位置为返回语句所在的结点时,则在所述插桩位置的最后一条指令之前安插所述显式内存释放指令;否则,在所述插桩位置的指令的最后一条指令之后安插所述显式内存释放指令。
2.根据权利要求1所述的方法,其特征在于,将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中的具体过程包括:
为所述直接死亡对象选择引用及确定插桩位置;
对于与所述直接死亡对象有域引用关系的所述间接死亡对象,根据为所述直接死亡对象选择的引用、所述域信息和域引用关系,确定所述间接死亡对象的引用及其插桩位置;
将所述直接死亡对象的引用及插桩位置以及所述间接死亡对象的引用及插桩位置,根据所述插桩类型条件进行判断,记录满足所述条件的引用及插桩位置,以获得所述插桩信息。
3.根据权利要求1或2所述的方法,其特征在于,所述插桩类型条件包括:在程序执行的控制流上,为所述死亡对象选择的引用所在的结点要支配所述死亡对象的插桩位置所在的结点。
4.根据权利要求2所述的方法,其特征在于,为所述直接死亡对象选择的引用包括:对象分配点处的引用和由phi语句、赋值语句或域赋值语句带来的引用中的至少一种;
当为所述直接死亡对象选择引用时,所述对象分配点处的引用的优先级大于所述由phi语句、赋值语句或域赋值语句带来的引用的优先级。
5.根据权利要求4所述的方法,其特征在于,根据所述插桩信息,创建相应的显式内存释放指令的具体过程包括:
对所述插桩信息中为所述死亡对象选择的引用进行判断;
当所述直接死亡对象的引用是所述对象分配点处的引用或是由phi语句或赋值语句带来的引用时,直接根据所述直接死亡对象的引用创建相应的显式内存释放指令,以对所述直接死亡对象的内存空间进行释放;或者,
当所述直接死亡对象的引用为所述域赋值语句带来的引用,则首先创建取域地址指令来获得存放所述直接死亡对象的引用的域的地址,然后根据所述域的地址新建加载指令来获得所述直接死亡对象的位置信息,最后根据所述位置信息创建显式内存释放指令,以对所述直接死亡对象的内存空间进行释放。
6.根据权利要求5所述的方法,其特征在于,所述方法还包括:
对于所述间接死亡对象的引用,首先创建取域地址指令来获得存放所述间接死亡对象的引用的域的地址,然后根据所述域的地址新建加载指令来获得所述间接死亡对象的位置信息,最后根据所述位置信息创建显式内存释放指令,以对与所述直接死亡对象有域引用关系的所述间接死亡对象的内存空间进行释放。
7.根据权利要求1所述的方法,其特征在于,所述死亡对象插桩位置包括:Java程序中方法的返回语句所在的结点和经过对象生命期分析后获得的对象死亡的位置中的至少一种;
当为所述死亡对象确定插桩位置时,所述返回语句所在的结点的优先级大于所述经过对象生命期分析后获得的对象死亡的位置的优先级。
8.一种即时编译器辅助的垃圾收集中显式释放对象的插桩装置,其特征在于,所述装置包括:
插桩信息收集模块,用于将经过对象生命期分析后获得的死亡对象信息中满足插桩类型条件的信息,记录到插桩信息中;所述插桩信息包含为死亡对象选择的引用及确定的插桩位置;所述死亡对象包括:直接死亡对象和与所述直接死亡对象有域引用关系的间接死亡对象;所述死亡对象信息包括:直接死亡对象及对应的死亡位置和所述直接死亡对象的域信息,以及与所述直接死亡对象有域引用关系的间接死亡对象及对应的死亡位置;
插桩信息执行模块,用于根据所述插桩信息,创建对应的显式内存释放指令,将所述显式内存释放指令安插到对应的位置上,以对所述死亡对象的内存空间进行释放;所述插桩信息执行模块将所述显式内存释放指令安插到对应的位置上的具体过程包括:
判断所述插桩信息中所述直接死亡对象或与所述直接死亡对象有域引用关系的所述间接死亡对象的插桩位置是否为分支跳转指令,当为分支跳转指令时,将所述显式内存释放指令安插在所述分支跳转指令的每个跳转目标的入口处;
否则,进一步判断所述直接死亡对象或所述间接死亡对象确定的插桩位置,当所述插桩位置为返回语句所在的结点时,则在所述插桩位置的最后一条指令之前安插所述显式内存释放指令;否则,在所述插桩位置的指令的最后一条指令之后安插所述显式内存释放指令。
CN2009100772519A 2009-01-20 2009-01-20 一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法 Expired - Fee Related CN101493767B (zh)

Priority Applications (1)

Application Number Priority Date Filing Date Title
CN2009100772519A CN101493767B (zh) 2009-01-20 2009-01-20 一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CN2009100772519A CN101493767B (zh) 2009-01-20 2009-01-20 一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法

Publications (2)

Publication Number Publication Date
CN101493767A CN101493767A (zh) 2009-07-29
CN101493767B true CN101493767B (zh) 2011-06-01

Family

ID=40924382

Family Applications (1)

Application Number Title Priority Date Filing Date
CN2009100772519A Expired - Fee Related CN101493767B (zh) 2009-01-20 2009-01-20 一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法

Country Status (1)

Country Link
CN (1) CN101493767B (zh)

Families Citing this family (7)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN101894049A (zh) * 2010-07-14 2010-11-24 中兴通讯股份有限公司 一种自适应回收垃圾对象的系统及方法
CN107967205B (zh) * 2016-10-18 2020-12-29 阿里巴巴(中国)有限公司 一种内存分析方法、装置、系统以及计算设备
CN108132863A (zh) * 2017-12-26 2018-06-08 东软集团股份有限公司 内存检测方法、装置、计算机可读存储介质及计算机
CN108804233A (zh) * 2018-06-27 2018-11-13 联想(北京)有限公司 内存空间回收方法、装置、电子设备及存储介质
CN110209397B (zh) * 2019-05-13 2023-09-01 矩阵元技术(深圳)有限公司 一种数据处理方法、装置及系统
CN110187884B (zh) * 2019-06-04 2020-10-27 中国科学技术大学 一种多线程应用场景下的访存指令插桩优化方法
CN116860658A (zh) * 2023-06-21 2023-10-10 中国科学院软件研究所 一种面向大数据处理框架的高效半自动垃圾回收方法和系统

Also Published As

Publication number Publication date
CN101493767A (zh) 2009-07-29

Similar Documents

Publication Publication Date Title
CN101493767B (zh) 一种即时编译器辅助的垃圾收集中显式释放对象的插桩方法
Lu et al. An incremental points-to analysis with CFL-reachability
US7912877B2 (en) Leveraging garbage collection to dynamically infer heap invariants
US7725883B1 (en) Program interpreter
US6675379B1 (en) Automatic removal of array memory leaks
WO2010014981A2 (en) Method and apparatus for detection and optimization of presumably parallel program regions
US8056061B2 (en) Data processing device and method using predesignated register
US20040181782A1 (en) System and method for optimizing memory usage by locating lingering objects
US7716657B2 (en) Compiler optimization with privatizing of a non-final object
WO2009055914A1 (en) Static analysis defect detection in the presence of virtual function calls
US9424004B2 (en) Execution guards in dynamic programming
US6675377B1 (en) Program conversion apparatus
von Dincklage et al. Converting Java classes to use generics
Nicolay et al. Detecting function purity in JavaScript
Hu et al. The role of return value prediction in exploiting speculative method-level parallelism
Sallenave et al. Lightweight generics in embedded systems through static analysis
Wimmer et al. Automatic feedback-directed object inlining in the java hotspot™ virtual machine
US7712093B1 (en) Determining intra-procedural object flow using enhanced stackmaps
Faxén Optimizing lazy functional programs using flow inference
Mazur et al. Practical aspects for a working compile time garbage collection system for Mercury
Higuera-Toledano et al. Analyzing the performance of memory management in RTSJ
Jones et al. A fast analysis for thread-local garbage collection with dynamic class loading
Würthinger et al. Array bounds check elimination in the context of deoptimization
Qian et al. A study of type analysis for speculative method inlining in a JIT environment
Antoniol et al. Impact of function pointers on the call graph

Legal Events

Date Code Title Description
C06 Publication
PB01 Publication
C10 Entry into substantive examination
SE01 Entry into force of request for substantive examination
C14 Grant of patent or utility model
GR01 Patent grant
CF01 Termination of patent right due to non-payment of annual fee

Granted publication date: 20110601

Termination date: 20150120

EXPY Termination of patent right or utility model