追踪内存访问的方法和装置的制作方法

文档序号:6357060阅读:191来源:国知局
专利名称:追踪内存访问的方法和装置的制作方法
技术领域
本发明涉及内存管理,更具体而言,涉及用于追踪内存访问的方法和装置。
背景技术
为了对计算机的内存进行分析和管理,常常需要追踪一个程序在运行期间对内存的访问纪录。内存的访问纪录可以用于进行例如内存泄露检测、数据竞争检测等多项内存分析。为了进行内存访问追踪,需要对程序进行初步处理,使其能够记录程序运行期间所涉及的内存访问事件。图I示出现有技术中用于执行内存访问追踪的方案的示意图。如图所示,为了追踪程序在运行期间的内存访问情况,首先对有待执行的程序进行附加代码的插入。所插入的附加代码包含一些指令,用于记录程序运行过程中对内存的读取、写入等动作。于是,在运行时环境中,不仅原始程序得到执行,所插入的代码也随之被执行。在执行时,所插入的·附加代码会调用内存访问追踪模块中的一些例行程序,从而对各个内存访问事件进行监视和记录,并将记录的内存访问事件存储在存储装置中,例如内存、数据库中。可以看到,在上述方案中,为了实现对内存访问的追踪,需要执行附加的代码,并在内存中临时存储各种内存访问事件。由于内存访问事件往往数量极为庞大,记录内存访问事件本身又额外地占用了大量的内存,带来了极大的内存开销。例如,按照上述方案对WAS (Websphere Application Server)应用进行动态内存分析所占用的内存是运行原有应用时所占用内存的3.3倍;为了追踪Tomcat应用的内存访问情况而占用的内存是原有应用的3-5倍。为了解决内存分析本身带来的巨大内存开销,现有技术中提出了一些方法来对上述方案进行改进。在一种方法中,为程序的运行和分析提供足够大的内存空间。但是,该方法仅仅是在学术研究领域较为流行,在现实中并不可行。在一种方法中,设计一种过滤工具,该工具根据一些算法和策略对一些不重要的内存访问事件进行过滤和移除,从而减轻内存访问事件的存储开销。然而,所提出的过滤算法和策略通常较为复杂,不易于广泛执行。在内存追踪的一种常用方法中,将内存访问事件直接写入文档之中用于事后分析。这样的方式虽然可以减轻内存存储的开销,但是却失去了内存实时分析的优势。尤其对于一些长时间运行的应用,例如Tomcat和WAS,并不适合进行事后分析。另一方面,现有的多种应用,例如上述的Tomcat和WAS,都是采用面向对象的语言进行编写。与传统的由一系列指令流程构成的面向过程的程序语言不同,面向对象的程序可以看到多个共同协作的对象的集合,其中每个对象能够接收消息、处理数据并将消息发送给其他对象。根据面向对象语言的特点,面向对象的应用程序对内存的访问可以看作程序中的对象的某些成员的内存操作。并且,许多面向对象编程语言已经被设计为具有垃圾收集功能。垃圾收集功能是一种自动的内存管理方案,用于对应用不再访问或使用的对象所占用的内存进行清理。在这种方案下,如果一个对象不被任何其他对象所引用,或者说,从根对象无法达到该对象,那么该对象及其占用的内存将作为垃圾被收集、移动和重新整理。虽然垃圾收集方案使得程序员不必再手动处理内存的分配和管理,但同时也使得内存的追踪更加困难。基于面向对象编程语言的诸多特点,希望提出一种针对性的内存追踪方案,使其能够减小内存追踪的存储开销,同时简单而易于执行。

发明内容
鉴于以上提出的问题,提出本发明,旨在提供一种针对面向对象语言的内存追踪方案,用以解决至少一个现有技术中存在的问题。根据本发明第一方面,提出一种方法,用于Java程序对内存的访问,所述方法包括通过修改面向对象的程序的核心类,为程序中的至少一个类分配ID ;向运行环境请求 与卸载相关的信息,利用返回的信息获得卸载的类的ID ;以及释放用于存储卸载的类的内存访问信息的内存空间。根据本发明第二方面,提出一种装置,用于追踪Java程序对内存的访问,所述装置包括代码修改模块,配置为通过修改面向对象的程序的核心类,为程序中的至少一个类分配ID ;以及卸载追踪模块,配置为向运行环境请求与卸载相关的信息,利用返回的信息获得卸载的类的ID,并释放用于存储卸载的类的内存访问信息的内存空间。利用本发明实施例的方法和装置,能够标识出面向对象的程序中的各个类,并获得卸载的类的信息,由此释放与卸载的类的内存访问有关的内存空间,从而极大地减小内存访问追踪的存储开销。


图I示出现有技术中用于执行内存访问追踪的方案的示意图;图2示出根据本发明一个实施例的方法的流程图;图3示出根据本发明一个实施例的修改核心类的示意图;图4示出根据一个实施例获得卸载的类的信息的步骤;以及图5示出根据本发明一个实施例的装置的示意图。
具体实施例方式下面结合具体例子描述本发明的实施方式。应该理解,出于说明目的而描述的例子不应作为对本发明实质范围的限制。在本发明的多个实施例中,提出一种方法,用于追踪由面向对象语言编写的程序对内存的访问。如本领域技术人员所知,面向对象的语言,例如Java,C#等,会在程序中定义对象所属于的各种类。相应地,在运行环境中存在类加载器,用以动态地加载所需的类。在运行环境判断认为不再需要一个已经加载的类时,会将这个类进行卸载。基于此,如果能够对卸载的类进行标识和识别,并相应地释放这些类的内存访问事件所占用的内存,那么就有可能在追踪内存访问的同时减轻内存的开销,提闻追踪效率。将上述构思作为出发点,本发明的实施例针对面向对象的语言提出一种内存追踪方法,从而在类卸载的情况下释放相应的内存占用,提闻内存追踪的空间效率。图2不出根据本发明一个实施例的方法的流程图。如图所示,在一个实施例中,追踪内存访问的方法包括以下步骤步骤20,通过修改面向对象的程序的核心类,为程序中的至少一个类分配ID ;步骤22,向运行环境请求与卸载相关的信息,利用返回的信息获得卸载的类的ID ;以及步骤24,释放用于存储卸载的类的内存访问信息的内存空间。具体地,为了获知卸载的类的信息,首先必须对类进行标识,也就是,为每个类分配一个独有的ID。因此,在步骤20中,对于面向对象的程序,通过在描述一般类的核心类中添加成员,以此成员的值来表示类的ID,从而实现对类的ID分配。这样的ID分配过程与面向对象的程序的特点密不可分。如本领域技术人员所知,在面向对象的程序中,对象是对现实世界中事物的抽象,是程序的基本封装单位。一般地,一个对象通过对象标识、属性和方法来刻画。对象属性是用来描述对象的静态特征的一组数据,在Java程序中又称为数据成员。对象方法也称为服
务或操作,是对对象动态特征(行为)的描述。每一个方法确定对象的一种行为或功能,又称为成员方法。如果一组对象具有相同的属性和方法,那么这组对象就构成一个类。因此,类是对象的抽象和归纳,对象是类的实例。类的概念使得编程人员能够对属于该类的全部对象进行统一的描述。因此,在面向对象的程序中,通常在定义对象之前应先定义类,在定义了类之后,才在类的基础上创建对象。与对象的特征相对应,描述一个类需要指明下述三个方面内容类的名称、属性说明和方法说明。在典型的面向对象语言Java的程序中,类通常以如下格式来定义class 类名{数据成员成员方法}其中花括号中包括的内容称为类体,由数据成员(属性)和成员方法(方法)两部分组成。与对象相对应,类体中的数据成员描述对象的属性;成员方法则刻画对象的行为或动作,每一个成员方法确定一个功能或操作。在一个面向对象的程序中,往往包括多个类,包括系统定义的类和用户自定义的类。其中系统定义的类或类库,是系统提供的已实现的标准类的集合,它提供了程序与其运行环境之间的接口。基于面向对象程序中类的上述结构和特点,可以在描述各个类的核心类中添加一个新的数据成员,通过定义成员方法使得上述数据成员特定于每个具体的类。由于如此定义的数据成员与类之间具有独有的对应关系,这样的数据成员可以用作标识类的ID。由此,就为程序中的类分配了其独有的ID。以下结合典型的面向对象语言Java描述步骤20的具体过程。在Java程序中,存在多个系统定义的类,其中Java. lang. Class是用于对类操作信息进行描述的一个特殊类,是java的一个核心类。相应地,程序中的其他自定义类都
--对应于这个Class类中的一个对象,一个Class类的对象用于描述一个自定义类,如自
定义类的类名、类加载器的名称等。图3示出根据本发明一个实施例的修改核心类的示意图,其中图3A示出原有的核心类Java. lang. Class的结构,图3B示出修改后的该核心类的结构。如图3A所示,与常规类定义相似,Java. lang. Class也包括数据成员和成员方法。为了给每个类分配其独有的ID,在一个实施例中,如图3B所示,在Java. lang. Class中添加新的数据成员objectID,并在成员方法中通过构造函数为objectID赋值,使得Class类的每个对象具有不同的objectID 值。在一个具体例子中,还在程序中添加一个计数器,使得每加载一个类,该计数器的值加I。并且,在上述成员方法中将objectID设定为等于计数器的值。如此,每当加载一个一般类,这个类作为上述Class的对象就具有了以上定义的objectID。并且,由于每次加载时的计数器值不相同,因此,每个类就获得了自己独有的objectID。在另一个具体例子中,在上述成员方法中将objectID设定为等于类的加载时间。由此,在依次加载各个类时,每个类的加载时间必然彼此不同,于是其获得的objectID就彼此不同,从而各自具有了独特的ID。在另一个具体例子中,在上述成员方法中将objectID设定为等于系统生成的 UUIDt5UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常,UUID至少包含以下几部分的组合当前日期和时间、时钟序列、唯一的IEEE机器识别号(例如网卡MAC地址)。由于UUID的唯一性,作为Class对象的每个类都会获得独有的objectlDo尽管以上描述了几个具体例子,但是为objectID赋值的构造函数并不限于此。可以理解,只要构造函数能够使得每个类获得的objectID彼此不同,就可以使用这样的构造函数来为objectID赋值。并且,尽管以上实施例都是结合java语言进行描述的,但是其他面向对象语言,例如C#,Smalltalk, Scala等,都存在类似的类的概念和定义,以及对一般类进行描述的特殊核心类。通过对该核心类进行与上述实施例相类似的修改,就可以为每个一般类分配一个独有的ID。在加载的类各自具有独有的ID的基础上,在步骤22中,利用从运行环境返回的卸载信息获得卸载的类的ID。以下仍然结合java语言来描述该步骤的具体实现过程。如前所述,Java具有自动的垃圾收集功能。在这种功能下,如果一个对象不被任何其他对象所引用,那么垃圾收集器就会清除该对象,并释放其占用的内存。为了使程序能更加灵活地控制对象的生命周期,进一步地,java把对象的引用分为四种级别,这四种级别由高到低依次为强引用、软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference)。本文之前提到的引用实际上都是指强引用,它是使用最普遍的引用。如果对一个对象使用强引用,那么垃圾回收器绝不会回收这个对象及其占用的内存。而如果对一个对象(称为referent)使用弱引用,则可以在维持与该对象的引用关系的同时,不阻止它被垃圾收集。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。弱引用可以和一个引用队列(ReferenceQueue)联合使用。如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。基于Java程序中垃圾回收和对象引用的上述特点,可以利用Java虚拟机返回的弱引用信息获得被回收的Class对象(对应于被卸载的类)的信息。具体地,图4示出根据一个实施例获得卸载的类的信息的步骤。如图4所示,可以通过如下步骤获取卸载的类的信息。在步骤220,为每个Class对象ο创建弱引用。所述弱引用是Java系统定义的弱弓丨用类Java. lang. ref. WeakReference的对象。如上所述,这样的弱引用并不会阻止Class对象ο被垃圾收集。并且,在步骤222,为每个弱引用对象设定ID,使其等于所引用的Class对象ο所具有的ID,即上述objectID。实际上,原有的弱引用类并不具备ID这一数据成员。为此,与上述对Class类的修改相似,对于弱引用类Java. lang. ref. WeakReference,也可以为其添加表示ID的数据成员,并在成员方法中将其ID的值定义为等于其引用的对象的ID。这样,每个弱引用对象就具备了与其引用的Class对象ο相同的ID。接着,在步骤224,将Class对象ο对应的弱引用对象添加到引用队列中。然后,在步骤226,在引用队列中调用removeO方法,从而向运行环境发出请 求,并等待运行环境的通知。实际上,调用remove ()方法会使得Java虚拟机检查引用队列中是否有弱引用的引用对象被垃圾收集;如果没有,则形成阻塞,继续等待;如果有,Java虚拟机会弹回被回收的对象所对应的弱引用对象的信息。因此,在步骤228, —旦某个Class对象ο被垃圾收集,remove O方法会返回对应的弱引用的ID。由于在步骤222中已经将弱引用的ID设定为被引用的Class对象ο的objectID,因此,在步骤228中实际上就可以获得被垃圾收集的Class对象的ID。进一步地,由于Java. lang. Class的一个特定对象对应于一个特定类,因此,被垃圾收集的Class对象就对应于被卸载的类,该Class对象的objectID也就是被卸载的类的ID。由此,通过利用对Class对象的弱引用和垃圾回收机制,可以从虚拟机提供的接口获得被卸载的类的ID0尽管以上结合Java程序的特点描述了获得被卸载的类的信息的具体过程,但是可以理解,对于其他面向对象的语言,在各个类被分配了独有的ID的基础上,都可以利用运行环境提供的接口和调用方法来获取被卸载的类的信息。在获得了被卸载的类的信息的基础上,就可以在图2的步骤24中,释放用于存储被卸载的类的内存访问信息的内存空间。具体地,一旦从运行环境中获知被卸载类的ID,就可以将与该类有关的内存访问信息移除,释放其占用的内存空间。与卸载的类有关的内存访问信息包括,该类的所有对象的内存访问事件、该类的各个变量(例如静态变量)的内存操作事件等等。由于移除了不必要的内存访问信息,释放了这些信息占用的大量内存空间,追踪内存访问所带来的额外内存开销就可以得到极大的降低。综合以上,在本发明的多个实施例中,在面向对象程序的核心类中为每个类分配了独有的ID,通过运行环境提供的接口获得卸载的类的ID,并相应地移除卸载的类的内存访问信息,从而实现了更为高效地内存访问追踪。基于同一发明构思,本发明的实施例还提供了追踪面向对象程序的内存访问的装置。图5示出根据本发明一个实施例的装置的示意图。为了更清楚地示出本发明的装置与现有技术的内存访问追踪系统的关系,图5的示意图基于图I的系统进行了修改,其中修改了原有的代码插入模块,形成了代码修改模块50,并新添加了卸载追踪模块52。在图5的装置中,代码修改模块50配置为,通过修改面向对象的程序的核心类,为程序中的至少一个类分配ID ;卸载追踪模块52配置为,向运行环境请求与卸载相关的信息,利用返回的信息获得卸载的类的ID,并释放用于存储卸载的类的内存访问信息的内存空间。具体地,代码修改模块50可以为面向对象的程序添加用于追踪内存访问事件的代码。在添加这样的代码的同时,代码修改模块50可以在描述各个类的核心类中添加一个新的数据成员,通过定义成员方法使得上述数据成员特定于每个具体的类,由此,为程序中的每个类分配其独有的ID。以Java语言为例,代码修改模块50可以将用于内存访问追踪的代码插入到原Java程序的适当位置,同时修改Java类库中用于对类操作信息进行描述的核心类Java,lang. Class。具体地,代码修改模块50在Java. lang. Class中添加新的数据成员objectID,并在成员方法中通过构造函数为objectID赋值,使得Class类的每个对象具有不同的objectID值。在不同例子中,构造函数的设定可以有所不同,例如将objectID初始化为等于程序中计数器的值,等于类的加载时间,等于系统生成的UUID等。通过以上的修改,使得每个Class对象具有不同的objectID值,相应地,每个类也就分配获得了独有的ID。在加载的类各自具有独有的ID的基础上,卸载追踪模块52利用从运行环境返回的卸载信息获得卸载的类的ID,并相应地释放用于存储卸载的类的内存访问信息的内存空间。
仍以Java为例,结合Java程序中垃圾回收和对象引用的特点,卸载追踪模块52可以利用运行环境返回的弱引用信息获得被回收的Class对象(对应于被卸载的类)的信息。具体地,卸载追踪模块52可以配置为执行以下步骤首先,为每个Class对象ο创建弱引用;并且,将弱引用对象的ID设定为Class对象ο所具有的ID。接着,将Class对象ο对应的弱引用对象添加到引用队列中,然后,在引用队列中调用removeO方法,从而向运行环境发出请求,并等待运行环境的通知。一旦某个Class对象ο被垃圾收集,removeO方法会返回对应的弱引用的ID。由于已经将弱引用的ID设定为被引用的Class对象ο的ID,因此,卸载追踪模块52就获得了被垃圾收集的Class对象的ID,也就是,被卸载的类的ID0在获得了被卸载的类的信息的基础上,卸载追踪模块52就可以将与该类有关的内存访问信息移除,释放其占用的内存空间,由此减小追踪内存访问事件所带来的额外内存开销。尽管以上结合Java语言进行了具体描述,但是可以理解,由于面向对象编程语言所具有的关于类、对象等的共同特征,本领域技术人员在阅读本说明书的情况下能够针对各种面向对象语言配置代码修改模块,使其为程序中的每个类分配ID,并根据各个语言的运行环境的接口特点配置卸载追踪模块,使其利用运行环境提供的接口获得卸载的类的信息,从而释放与卸载的类有关的内存空间。通过以上结合具体例子的描述可以获知,利用本发明实施例的追踪内存访问的方法和装置,可以在追踪面向对象的程序对内存的访问事件的同时减小内存的额外开销,提高内存的空间效率。本领域技术人员可以理解,上述追踪内存访问的方法和装置可以使用计算机可执行指令和/或包含在处理器控制代码中来实现,例如在诸如磁盘、⑶或DVD-ROM的载体介质、诸如只读存储器(固件)的可编程的存储器或者诸如光学或电子信号载体的数据载体上提供了这样的代码。各实施例的装置及其模块可以由诸如超大规模集成电路或门阵列、诸如逻辑芯片、晶体管等的半导体、或者诸如现场可编程门阵列、可编程逻辑设备等的可编程硬件设备的硬件电路实现,也可以用由各种类型的处理器执行的软件实现,也可以由上述硬件电路和软件的结合实现。用于执行本发明的操作的软件和程序代码,可以用一种或多种程序设计语言的组合来编写,包括但不限于,面向对象的程序设计语言,诸如Java,Smalltalk, C++之类,以及常规的过程式程序设计语言,诸如C程序设计语言或类似的程序设计语言。程序代码可以本地地或远程地在计算机上执行,以完成设定的操作。虽然以上结合具体实施例,对本发明的追踪内存访问的方法和装置进行了详细描述,但本发明并不限于此。本领域普通技术人员能够在说明书教导之下对本发明进行多种变换、替换和修改而不偏离本发明的精神和范围。应该理解,所有这样的变化、替换、修改仍然落入本发明的保护范围之内。本发明的保护范围由所附权利要求 来限定。
权利要求
1.一种方法,用于追踪Java程序对内存的访问,所述方法包括 通过修改Java程序的核心类,为程序中的至少一个类分配ID ; 向运行环境请求与卸载相关的信息,利用返回的信息获得卸载的类的ID ;以及 释放用于存储卸载的类的内存访问信息的内存空间。
2.根据权利要求I的方法,其中所述核心类为Java.lang. Class,其中修改所述核心类包括,添加数据成员objectID,并在对应的成员方法中对objectID赋值,使得与所述核心类的对象相对应的类具有不同的objectID值作为其ID。
3.根据权利要求2的方法,其中对objectID赋值包括以下方法中之一 将objectID值设定为所述程序中的计数器的值,所述计数器的值随着类的加载而递增; 将objectID值设定为类的加载时间; 将objectID值设定为系统生成的UUID。
4.根据权利要求I的方法,其中获得卸载的类的ID包括 为至少一个核心类对象创建弱引用; 将弱引用对象的ID设定为所述核心类对象所具有的ID ; 将所述弱引用对象添加到引用队列中; 在引用队列中调用特定函数方法从而向运行环境请求与卸载相关的信息,所述函数方法请求返回与被垃圾收集的核心类对象相对应的弱引用对象信息;以及从运行环境获取返回的弱引用对象的ID,将此ID作为卸载的类的ID。
5.根据权利要求I的方法,其中释放用于存储卸载的类的内存访问信息的内存空间包括移除与所述卸载的类有关的内存访问信息,所述内存访问信息包括,该类的所有对象的内存访问事件、该类的各个变量的内存操作事件。
6.一种装置,用于追踪Java程序对内存的访问,所述装置包括 代码修改模块,配置为通过修改Java程序的核心类,为程序中的至少一个类分配ID ;以及 卸载追踪模块,配置为向运行环境请求与卸载相关的信息,利用返回的信息获得卸载的类的ID,并释放用于存储卸载的类的内存访问信息的内存空间。
7.根据权利要求6的装置,其中所述核心类为Java.lang. Class,其中所述代码修改模块配置为,在所述核心类中添加数据成员objectID,并在对应的成员方法中对objectID赋值,使得与所述核心类的对象相对应的类具有不同的objectID值作为其ID。
8.根据权利要求7的装置,其中所述代码修改模块配置为执行以下之一 将objectID值设定为所述程序中的计数器的值,所述计数器的值随着类的加载而递增; 将objectID值设定为类的加载时间; 将objectID值设定为系统生成的UUID。
9.根据权利要求6的装置,其中所述卸载追踪单元配置为 为至少一个核心类对象创建弱引用; 将弱引用对象的ID设定为所述核心类对象所具有的ID ; 将所述弱引用对象添加到引用队列中;在引用队列中调用特定函数方法从而向运行环境请求与卸载相关的信息,所述函数方法请求返回与被垃圾收集的核心类对象相对应的弱引用对象信息;以及从运行环境获取返回的弱引用对象的ID,将此ID作为卸载的类的ID。
10.根据权利要求6的装置,其中所述卸载追踪模块配置为移除与所述卸载的类有关的内存访问信息,所述内存访问信息包括,该类的所有对象的内存访问事件、该类的各个变量的内存操作事件。
全文摘要
提供一种用于追踪面向对象的程序对内存的访问的方法和装置,所述方法包括通过修改面向对象的程序的核心类,为程序中的至少一个类分配ID;从运行环境获得卸载的类的ID;以及释放用于存储卸载的类的内存访问信息的内存空间。所述装置与所述方法相对应。利用上述方法和装置,能够标识出面向对象的程序中的各个类,并获得卸载的类的信息,由此释放与卸载的类的内存访问有关的内存空间,从而极大地减小内存访问追踪的存储开销。
文档编号G06F11/34GK102722432SQ20111007758
公开日2012年10月10日 申请日期2011年3月29日 优先权日2011年3月29日
发明者刘伟, 罗志达 申请人:国际商业机器公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1