内存泄漏检测方法和装置与流程

文档序号:11063415阅读:468来源:国知局
内存泄漏检测方法和装置与制造工艺

本发明涉及内存检测领域,特别是涉及一种内存泄漏检测方法和装置。



背景技术:

内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的,使用完后必须显示释放的内存。应用程序一般使用malloc、realloc、new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,也就是这块内存泄漏了。

在常规的客户端性能专项测试中,内存使用情况是重点关注的一个指标。Windows调试工具集(the Debugging Tools for Windows)中的UMDH工具能监控到内存泄漏的问题点,得到内存泄漏处的堆栈。UMDH监控Windows系统函数中的内存分配函数,通过统计比较使用者手动指定的开始和结束两个时间点内存分配情况得到这两个时间点之间的内存泄漏,然而手动指定难以精确,且得到的数据不全面。



技术实现要素:

基于此,有必要针对传统的内存泄漏检测手动指定时间点不够精确且得到的数据不全面的问题,提供一种内存泄漏检测方法,监控的时间点精确且监控的数据全面。

此外,还有必要提供一种内存泄漏检测装置,监控的时间点精确且监控的数据全面。

一种内存泄漏检测方法,包括以下步骤:

在被测进程启动时加载预设的内存监控动态链接库;

通过所述内存监控动态链接库挂钩内存分配函数和/或windows系统应用程序编程接口;

通过挂钩函数调用所述内存分配函数和/或windows系统应用程序编程接 口,收集被测进程启动时的内存分配信息,以及收集被测进程退出时的内存分配信息;

对所述被测进程启动时的内存分配信息及退出时的内存分配信息进行比较得出内存泄漏信息。

一种内存泄漏检测装置,包括:

加载模块,用于在被测进程启动时加载预设的内存监控动态链接库;

挂钩模块,用于通过所述内存监控动态链接库挂钩内存分配函数和/或windows系统应用程序编程接口;

收集模块,用于通过挂钩函数调用所述内存分配函数和/或windows系统应用程序编程接口,收集被测进程启动时的内存分配信息,以及收集被测进程退出时的内存分配信息;

比较模块,用于对所述被测进程启动时的内存分配信息及退出时的内存分配信息进行比较得出内存泄漏信息。

上述内存泄漏检测方法和装置,通过在被测进程启动时加载内存监控动态链接库,通过内存监控动态链接库挂钩内存分配函数,收集被测进程启动时的内存分配信息和退出后的内存分配信息,通过比较被测进程启动时和退出时内存分配信息,得出内存泄漏信息,监控了被测进程从启动开始到退出的整个过程中的内存分配信息,监控的时间点精确,得到的数据全面,且统计出来的结果是始终没有释放的,则得出的内存泄漏信息更加准确。

附图说明

图1A为一个实施例中终端的内部结构示意图;

图1B为一个实施例中服务器的内部结构示意图;

图2为一个实施例中内存泄漏检测方法的流程图;

图3为函数调用关系示意图;

图4为一个实施例中将内存监控信息发送给数据处理进程,由该数据处理进程对该内存监控信息进行处理的具体流程图;

图5A为被测进程与数据处理进程传输数据的示意图;

图5B为进程间传输数据的过程示意图;

图6为数据缓存示意图;

图7为高速缓存机制示意图;

图8为内存快照中内存地址比较示意图;

图9为内存快照中采用函数堆栈比较的示意图;

图10为一个堆栈的变化曲线图;

图11为一个实施例中内存泄露检测装置的结构框图;

图12为另一个实施例中内存泄露检测装置的结构框图;

图13为另一个实施例中内存泄露检测装置的结构框图;

图14为另一个实施例中内存泄露检测装置的结构框图。

具体实施方式

为了使本发明的目的、技术方案及优点更加清楚明白,以下结合附图及实施例,对本发明进行进一步详细说明。应当理解,此处所描述的具体实施例仅仅用以解释本发明,并不用于限定本发明。

可以理解,本发明所使用的术语“第一”、“第二”等可在本文中用于描述各种元件,但这些元件不受这些术语限制。这些术语仅用于将第一个元件与另一个元件区分。举例来说,在不脱离本发明的范围的情况下,可以将第一客户端称为第二客户端,且类似地,可将第二客户端称为第一客户端。第一客户端和第二客户端两者都是客户端,但其不是同一客户端。

图1A为一个实施例中终端的内部结构示意图。如图1A所示,该终端包括通过系统总线连接的处理器、存储介质、内存、网络接口、声音采集装置、显示屏、扬声器和输入装置。其中,终端的存储介质存储有操作系统,还包括一种内存泄漏检测装置,该内存泄漏检测装置用于实现一种内存泄漏检测方法。该处理器用于提供计算和控制能力,支撑整个终端的运行。终端中的内存为存储介质中的内存泄漏装置的运行提供环境,网络接口用于与服务器进行网络通信,如发送游戏数据请求至服务器,接收服务器返回的游戏数据等。终端的显示屏可以是液晶显示屏或者电子墨水显示屏等,输入装置可以是显示屏上覆盖的触 摸层,也可以是终端外壳上设置的按键、轨迹球或触控板,也可以是外接的键盘、触控板或鼠标等。该终端可以是手机、平板电脑或者个人数字助理。本领域技术人员可以理解,图1A中示出的结构,仅仅是与本申请方案相关的部分结构的框图,并不构成对本申请方案所应用于其上的终端的限定,具体的终端可以包括比图中所示更多或更少的部件,或者组合某些部件,或者具有不同的部件布置。

图1B为一个实施例中服务器的内部结构示意图。如图1B所示,该服务器包括通过系统总线连接的处理器、存储介质、内存、网络接口、显示屏和输入装置。其中,该服务器的存储介质存储有操作系统、数据库和内存泄漏检测装置,数据库中存储有游戏场景数据、游戏用户数据等,该内存泄漏装置用于实现适用于服务器的一种内存泄漏检测方法。该服务器的处理器用于提供计算和控制能力,支撑整个服务器的运行。该服务器的内存为存储介质中的内存泄漏装置的运行提供环境。该服务器的显示屏可以是液晶显示屏或者电子墨水显示屏等,输入装置可以是显示屏上覆盖的触摸层,也可以是终端外壳上设置的按键、轨迹球或触控板,也可以是外接的键盘、触控板或鼠标等。该服务器的网络接口用于据以与外部的终端通过网络连接通信,比如接收终端发送的游戏数据请求以及向终端返回游戏数据等。服务器可以用独立的服务器或者是多个服务器组成的服务器集群来实现。本领域技术人员可以理解,图1B中示出的结构,仅仅是与本申请方案相关的部分结构的框图,并不构成对本申请方案所应用于其上的服务器的限定,具体的服务器可以包括比图中所示更多或更少的部件,或者组合某些部件,或者具有不同的部件布置。

内存泄漏是指应用程序使用malloc、realloc、new等函数从堆中分配到一块内存,使用完后,未调用free或delete函数释放该内存块导致的。传统的内存泄漏检测方法比较的是使用者指定的两个时间点的内存泄漏情况,而有些应用程序在退出后才释放分配的内存,导致检测的内存泄漏数据不全面,也不准确。本发明中的内存泄漏检测方法,通过在被测进程启动时加载内存监控动态链接库并执行其中的函数,在被测进程启动时开始监控内存分配信息,在被测进程退出时仍然执行加载后的内存监控动态链接库监控被测进程退出后的内存分配 信息,通过比较被测进程启动时和退出时内存分配信息,得出内存泄漏信息,监控了被测进程从启动开始到退出的整个过程中的内存分配信息,监控的时间点精确,得到的数据全面,且得出内存泄漏信息更加准确。

其中,malloc函数,即memory allocation,动态内存分配函数,若分配成功,则返回指向被分配内存的指针,否则返回空指针。

realloc函数,即reset allocation,动态内存调整函数,若重新分配成功,则返回指向被分配内存的指针,否则返回空指针。

new相当于一个函数,在内存开辟完空间后,返回这个空间的首地址。

free或delete函数,释放内存。

内存分配信息可包括内存分配地址、内存分配大小和内存分配的函数堆栈,或者包括内存分配大小和内存分配的函数堆栈,或者包括内存分配地址和内存分配大小。

图2为一个实施例中内存泄漏检测方法的流程图。如图2所示,一种内存泄漏检测方法,包括以下步骤:

步骤202,在被测进程启动时加载预设的内存监控动态链接库。

具体地,首先采用静态注入的方法,在启动被测进程前使用detours工具包中的setdll.exe修改进程的可执行文件(.exe文件),使被测进程启动时自动加载预设的内存监控动态链接库(DLL,Dynamic Link Library),从而hook(挂钩)内存分配函数和/或windows系统API(Application Programming Interface,应用程序编程接口)。

步骤204,通过该内存监控动态链接库挂钩内存分配函数和/或windows系统应用程序编程接口。

具体地,在内存监控动态链接库中利用微软提供的detours库来实现hook(挂钩)C/C++标准库中的内存分配函数(malloc、free和realloc等)的功能。或者,hook windows系统应用程序编程接口,即hook更底层的HeapAlloc系列函数(包括HeapAlloc、HeapFree和HeapRealloc)或hook底层的系统函数(RtAllocateHeap等)。HeapAlloc函数用来在指定的堆上分配内存,并且分配后的内存不可移动。采用hook C/C++标准库中的内存分配函数,能大量去掉 windows系统函数的影响,提高结果的精确性。

此外,还可同时在hook内存分配函数和windows系统应用程序编程接口之间切换。

在对malloc/free两个函数进行hook的过程中,可能会出现找不到地址的情况,其原因跟标准库的链接方式有关系,标准库函数既可以从一个libc*.lib静态链接,也可以从一个msvcrt*.dll动态链接到程序中。当静态链接到程序时,一个程序得到它自己私有的版本的标准库函数;当动态链接程序时,一个程序共享DLL中版本的静态标准库函数。在获取malloc/free地址时需进行区分。

图3为函数调用关系示意图。如图3所示,new分配内存后,通过调用malloc函数、heapalloc函数收集内存分配信息。

步骤206,通过挂钩函数调用该内存分配函数和/或windows系统应用程序编程接口,收集被测进程启动时的内存分配信息,以及收集被测进程退出时的内存分配信息。

具体地,内存监控动态链接库主要监控内存中malloc操作、free操作,包括内存分配的大小、内存分配的地址、内存释放的地址、内存分配的函数堆栈等。hook函数调用内存分配函数malloc或free,通过记录malloc操作和free操作,从而收集被测进程启动时的内存分配信息。该内存分配信息可包括内存分配地址、内存分配大小和内存分配的函数堆栈,或者包括内存分配大小和内存分配的函数堆栈,或者包括内存分配地址和内存分配大小。

malloc用于收集内存分配信息。free函数用于收集内存释放信息。内存释放信息可包括内存释放地址和/或内存释放的函数堆栈等。

malloc系列函数的申明如下:

void*__cdecl malloc(_In_size_t_Size);

void__cdecl free(_Inout_opt_void*_Memory);

void*__cdecl realloc(_In_opt_void*_Memory,_In_size_t_NewSize);

从malloc系列函数申明中可以看出,地址和大小从函数调用的参数或返回值可以直接获得,堆栈则需要调用dbghelp.dll中提供的函数获取,具体方法是调用系统函数LoadLibrary()加载dbghelp.dll,调用如下:

HMODULE m_dbgHelp=LoadLibrary(“dbghelp.dll”);

然后通过函数GetProcAddress()获取DLL中具体函数地址。

具体有两个函数可以获取堆栈,CaptureStackBackTrace()和StackWalk64()。两者有些区别,CaptureStackBackTrace是windows系统函数,调用一次可获取全部堆栈,速度快;StackWalk64调用一次只能获取一层堆栈,使用时需要循环调用才能获取全部堆栈,速度慢,能获取的堆栈较多。

例如,在funcB中调用CaptureStackBackTrace(),获得的堆栈为:

funcB()

funcA()

main()

若在funcB中调用StackWalk64(),只能获得funcB(),需要再调用一次获得funcA(),循环调用,直到不能再获取堆栈。

在个实施例中,上述内存泄漏检测方法还包括:采用堆栈全局获取函数(如CaptureStackBackTrace)获取堆栈,如果获取失败,则再采用堆栈局部获取函数(如StackWalk64)获取一次。

步骤208,对该被测进程启动时的内存分配信息及退出时的内存分配信息进行比较得出内存泄漏信息。

具体地,该内存分配信息可包括内存分配地址、内存分配大小和内存分配的函数堆栈,或者包括内存分配大小和内存分配的函数堆栈,或者包括内存分配地址和内存分配大小。

在一个实施例中,可将被测进程启动时内存分配信息中的内存分配地址和退出时的内存分配信息中的内存分配地址进行比较,判断被测进程退出时内存分配信息中比被测进程启动时内存分配信息中多的内存地址,则多的内存地址为内存泄漏地址,再得出内存泄漏大小。

在另一个实施例中,可将被测进程启动时内存分配信息的函数堆栈和退出时的内存分配信息中的函数堆栈进行比较,判断被测进程退出时内存分配信息中比被测进程启动时内存分配信息中多的函数堆栈,多的函数堆栈为函数堆栈内存泄漏。

在一个实施例中,对多的函数堆栈按照对应的函数地址大小进行排序,再对排序后的函数堆栈进行分类统计,得出函数堆栈内存泄漏次数和函数内存泄漏大小。

具体地,将多的函数堆栈进行分类,统计同一类的函数堆栈的泄漏次数,并计算出所有函数堆栈内存泄漏大小。

采用函数堆栈比较实际是一组数的比较,只有组内每一个数都相等才算相同的函数堆栈,判断两个函数堆栈是否相等需要每个数都比较,大量堆栈查找的时候比较耗时,采用给函数堆栈排序,可将函数堆栈按照函数地址从大到小排序,或从小到大排序。排序处理后的函数堆栈通过使用std::set数据结构实现红黑树管理,使查找时间复杂度降为O(lgn),极大地缩短了查找时间。

此外,还可统计出内存泄漏的代码行、严重程度、内存分配的函数堆栈的变化趋势等,通过函数堆栈的变化趋势更好的定位内存泄漏问题。

上述内存泄漏检测方法,通过在被测进程启动时加载内存监控动态链接库,通过内存监控动态链接库挂钩内存分配函数,收集被测进程启动时的内存分配信息和退出后的内存分配信息,通过比较被测进程启动时和退出时内存分配信息,得出内存泄漏信息,监控了被测进程从启动开始到退出的整个过程中的内存分配信息,监控的时间点精确,得到的数据全面,且统计出来的结果是始终没有释放的,则得出的内存泄漏信息更加准确。

在一个实施例中,上述内存泄漏检测方法还包括:通过挂钩函数调用该内存分配函数和/或windows系统应用程序编程接口,收集被测进程启动后的内存监控信息;将该内存监控信息发送给数据处理进程,由该数据处理进程对该内存监控信息进行处理。

具体地,内存监控信息可为内存分配信息或内存释放信息。内存分配信息可包括内存分配地址、内存分配大小和内存分配的函数堆栈,或者包括内存分配大小和内存分配的函数堆栈,或者包括内存分配地址和内存分配大小。内存释放信息可包括内存释放地址和/或内存释放的函数堆栈等。

数据处理进程对内存监控信息进行处理,收集到的内存监控信息主要是两 种,一种是分配内存,一个是释放内存。分配内存时,记录内存分配信息,释放内存时,删除记录的内存分配信息,如此统计得到的数据就是当前分配未释放的内存。

数据处理进程对内存监控信息进行处理,记录的数据结构可采用std::map,因map是红黑树实现的,适合做频繁的查找、插入和删除操作,这三种操作的时间复杂度都是O(lgn)。具体的数据结构申明如下:

其中,map的键值(key)是分配内存的地址。

通过比较两个记录点的内存分配信息,分析出在这两个时间点之间新分配出来且没有释放的内存,内存泄漏在其中。本实施例中测试所用的时间点是被测进程启动时和被测进程退出后,这样统计出来的结果是始终没有释放的,可以认定就是内存泄漏,准确率很高。

因数据收集与处理都写在内存动态链接库中,但由于数据处理耗时较多,可能导致进程变得严重卡顿,将收集到的数据发送给另一个进程处理和输出结果,可减轻被测进程的负担,避免出现卡顿,且能提高数据处理效率。

图4为一个实施例中将内存监控信息发送给数据处理进程,由该数据处理进程对该内存监控信息进行处理的具体流程图。如图4所示,将内存监控信息发送给数据处理进程,由该数据处理进程对该内存监控信息进行处理的步骤包括:

步骤402,将该内存监控信息写入共享内存中。

步骤404,通过该数据处理进程的监控线程读取该共享内存中的内存监控信息,并存入该数据处理进程的缓存中。

步骤406,通过该数据处理进程的分配线程从该缓存中取出内存监控信息, 并分配给该数据处理进程的处理线程进行处理。

具体地的,处理线程可采用多个,如5个、8个等。对于四核以下的中央处理器可相应的减少处理线程数。本实施例中,采用5个处理线程,采用多个处理线程可提高处理效率。

步骤408,通过该数据处理进程的刷新线程对该被测进程的内存监控信息处理得到的内存分配信息进行刷新。

刷新线程主要用于解析函数堆栈中的函数名和函数所在的模块名等。

被测进程将收集的内存监控信息发送给数据处理进程,涉及到进程间通信,可采用共享内存的方式实现数据的传输,采用共享内存传输速度快、不易出错。

图5A为被测进程与数据处理进程传输数据的示意图。如图5A所示,被测进程中注入内存监控动态链接库,调用malloc/free函数、收集相关数据、存储收集的数据,将存储的数据传输给数据处理进程,数据处理进程进行数据存储及处理、泄漏过滤与分析、内存泄漏结果输出、过滤设置、监控内核设置等。

图5B为进程间传输数据的过程示意图。如图5B所示,被测进程收集到内存分配和释放操作,内存监控动态链接库就会将收集的数据写入到共享内存中,数据处理进程(如TMemAnalyzer.exe)中的监控线程,发现共享内存中有数据就会读取出来。

图6为数据缓存示意图。如图6所示,在数据处理进程中增加缓存,用来暂时存放被测进程发送过来的数据(即监控线程从共享内存中读取出来的数据),将原来数据收到一个处理一个的同步模式改为数据收集与数据处理分开的异步模式,数据处理对数据的收集不会造成太大影响,则被测进程收集到数据就可以发送给数据处理进程,可避免被测进程传输数据慢而卡顿。缓存的数据结构是一个先进先出的队列,可以保证数据处理的顺序正确。

由于malloc和free在数据处理阶段是两个互逆的过程(malloc插入数据,free删除数据),如果在数据处理阶段之前能找到对应的malloc和free数据(对应是指同一内存地址),这两个互逆的操作就能省去,提高效率,为此在缓存中加入了一个高速缓存(cache)。每当一个free数据进入缓存时从高速缓存中查找对应的malloc数据,如果找到对应的malloc数据,则删除free数据及对应的 malloc数据。高速缓存的大小可根据需要设定,如可为64个数据大小、128个数据大小、256个数据大小等。采用128个数据大小的高速缓存,可避免容易过大每次查找耗时,以及容量过小命中率下降的问题。

在一个实施例中,上述内存泄漏方法还包括:当该内存监控信息为内存释放信息时,根据该内存释放信息从该数据处理进程的高速缓存中查找是否存在对应的内存分配信息,若是,则删除该内存释放信息及对应的内存分配信息,若否,则从缓存中查找是否存在对应的内存分配信息,若存在则删除该内存释放信息及对应的内存分配信息。

图7为高速缓存机制示意图。如图7所示,被测进程收集的数据同步写入共享内存中,然后数据处理进程从共享内存中读取数据并暂存到缓存中,读取的数据为内存释放信息,则从自身缓存中的高速缓存中查找是否存在对应的内存分配信息,若存在,则删除该内存释放信息及对应的内存分配信息,若不存在,则从缓存中查找是否存在对应的内存分配信息,若存在则删除该内存释放信息及对应的内存分配信息。

为了获取某个时刻的内存分配信息,可使用内存快照。内存快照是在某个时刻malloc分配了而一直没有调用free进行释放的内存的集合。在整个应用程序运行的生命周期中,每个时刻的内存快照基本都会有所变化,因为随着应用程序不断被使用者不断的操作,内存一直在发生着变化。获取到每个时刻的内存快照,可以知道两个内存快照之间发生了哪些内存操作,即两个内存快照之间发生了内存泄漏,通过两个内存快照的比对得到内存泄漏的内存地址。

图8为内存快照中内存地址比较示意图。如图8所示,在功能开始的时候采集快照1,功能结束的时候采集快照2,经过这个过程发现快照1的0x2进行了释放操作,所以在快照2中已经不存在,0x1依然在快照2中存在,则说明0x1没有被释放,0x3和0x4则是在快照2中才有,则说明0x3和0x4内存块是在该功能中分配出来的,且功能结束后还没有及时释放,则这两个内存块为泄漏内存块。

图9为内存快照中采用函数堆栈比较的示意图。如图9所示,在功能开始 的时候采集快照1,功能结束的时候采集快照2,经过这个过程发现,进入功能后,释放了前序场景不使用的内存块,即将快照1的0x2进行了释放操作,0x1依然在快照2中存在,则说明0x1没有被释放,0x3和0x4是离开功能后又回到前序场景界面,程序又分配了相关内存块,在快照1中是有这两内存块地址对应的函数堆栈的。0x5是快照2中才有的,进入功能后又分配的相关内存块,且功能结束后还没有及时释放,则内存块0x5为泄漏的堆栈。

下面结合具体的应用场景说明本发明的内存泄漏检测方法的过程。以游戏程序中,玩家从一个场景进入另一个场景,在另一个场景结束后重返原来的场景,其内存泄漏检测包括:

(a1)在进入一次被测场景后退出到前序场景开始收集第一内存快照,该第一内存快照记录当前时刻的内存分配信息;

(a2)在再次进入到被测场景后退出到前序场景开始收集第二内存快照,该第二内存快照记录当前时刻的内存分配信息;

(a3)将第一内存快照记录的内存分配信息及第二内存快照记录的内存分配信息进行比较,得出被测场景的内存泄漏信息。

采用a1到a3步骤,时间点可由玩家设置,满足玩家的个性化需求。

或者,其内存泄漏检测包括:

(b1)在游戏程序启动时收集收集第一内存快照,该第一内存快照记录当前时刻的内存分配信息;

(b2)在游戏程序退出时收集第二内存快照,该第二内存快照记录当前时刻的内存分配信息;

(b3)将第一内存快照记录的内存分配信息及第二内存快照记录的内存分配信息进行比较,得出被测场景的内存泄漏信息。

采用b1到b3步骤,采用的是游戏程序从启动到退出后收集的两个快照,比较这两个快照得出的泄漏更加准确。

内存分配信息包括内存分配地址、内存分配大小和内存分配函数堆栈。

将第一内存快照记录的内存分配信息及第二内存快照记录的内存分配信息进行比较的步骤包括:将该第一内存快照记录的内存分配信息中的函数堆栈和 第二内存快照记录的内存分配信息中的函数堆栈进行比较,判断该第二内存快照记录的内存分配信息中比该第一内存快照记录的内存分配信息中多的函数堆栈,则多的函数堆栈为函数堆栈内存泄漏。

进一步的,对多的函数堆栈按照对应的函数地址大小进行排序,再对排序后的函数堆栈进行分类统计,得出函数堆栈内存泄漏次数和函数内存泄漏大小。

具体地,将函数堆栈进行分类,统计同一类的函数堆栈的泄漏次数,并计算出所有函数堆栈内存泄漏大小。

采用函数堆栈比较实际是一组数的比较,只有组内每一个数都相等才算相同的函数堆栈,判断两个函数堆栈是否相等需要每个数都比较,大量堆栈查找的时候比较耗时,采用给函数堆栈排序,可将函数堆栈按照函数地址从大到小排序,或从小到大排序。排序处理后的函数堆栈通过使用std::set数据结构实现红黑树管理,使查找时间复杂度降为O(lgn),极大地缩短了查找时间。

通过上述内存泄漏检测方法把内存泄漏信息详细的统计出来,包括泄漏的额函数堆栈(可详细到代码行)、大小、次数。因malloc/free除了程序员本身使用外,C运行时库的函数也会大量使用,比如fopen_s,下面是其调用堆栈。

.exe!malloc(unsigned int size=24)Line87

.exe!_malloc_crt(unsigned int cb=24)Line44

.exe!_mtinitlocknum(int locknum=19)Line274

.exe!_getstream()Line71

.exe!_fsopen(const char *file=0x00546244,const char* mode=0x00546240,int shflag=128)Line61

.exe!fopen_s(_iobuf**pfile=0x001df994,const char*file=0x00546244,const char*mode=0x00546240)Line160

.exe!main(int argc=1,char**argv=0x00662b98)Line32

为此,将内存泄露信息通过关键字进行过滤。关键字可为预先设定的关键字。通过关键字筛选出匹配的内存泄露信息进行展示。

上述内存泄露检测方法还包括:将堆栈的泄露严重程度按照从大到小进行排序,将泄露严重程度排序在前的预定数量的堆栈展示出来。

泄露严重程度是指泄露程度大于预设的百分比。例如同一堆栈泄露大小大于第一预设值;同一堆栈泄露次数大于第二预设值;同一堆栈根据时间轴泄露大小呈上升趋势,上升趋势超过第三预设值,如30%;同一堆栈中分配相同大小内存块占总分配块的占比程度大于第四预设值,如50%。

在一个实施例中,上述内存泄露检测方法还包括:将每个堆栈的变化曲线图展示出来,让开发人员更加准确的判断泄露过程。图10为一个堆栈的变化曲线图。如图10所示,每隔预定间隔时间的堆栈泄露的次数,横坐标为时间,单位为秒,纵坐标为泄露次数。

在一个实施例中,上述内存泄露检测方法还包括:提供用户交互界面,并在用户交互界面上设置触发控件。通过用户交互界面上的触发控件,可完成内存泄露检测,不需要用户输入不同的命令,操作简单。

上述内存泄露检测方法可应用于任意应用程序,对任何应用程序的性能没有影响,采用系统API层及标准库层灵活配置的方式进行hook,加上过滤策略及严重程度定义,提高了检测结果的准确性和可读性,可兼顾游戏程序及其他应用程序,不需代码,可一次性检测,及对单个场景分析。

上述内存泄露检测方法应用于《铁骑》,在客户端性能测试过程中发现场景(64个机器人医师在江陵城内反复放置香炉15分钟,完成后自动退出)存在明显的内存泄露。分析结果:在进入和退出江陵城前后各截取两个快照,通过对比,在整个15分钟的测试过程中共泄露335.8M(兆),过了3分钟左右内存释放了290.5M,直至退出该场景前内存未发生变化,也就是退出场景前造成了45.3M的内存泄露。在测试中,有216个相关堆栈,共调用7023次,泄露47536935字节。

上述内存泄露检测方法应用于《怪物猎人》,在测试过程中内存泄露最明显的可以通过内存管理器看出存在内存泄露。分析结果:通过分析这类泄露来自程序中的flash文件的泄露,目前scaleform的泄露都是属于这类,其调用过程为:引擎中在flashplayer instance.cpp中调用主swf文件,swf文件中调用scaleform。因此与UI相关的swf中有内存泄露,堆栈都会调用到scaleform中去。

图11为一个实施例中内存泄露检测装置的结构框图。如图11所示,一种内存泄漏检测装置,对应于内存泄露检测方法所构建的功能模块架构,描述不详细之处参考方法描述,包括加载模块1102、挂钩模块1104、收集模块1106、比较模块1108。其中:

加载模块1102用于在被测进程启动时加载预设的内存监控动态链接库。

具体地,首先采用静态注入的方法,在启动被测进程前使用detours工具包中的setdll.exe修改进程的可执行文件(.exe文件),使被测进程启动时自动加载预设的内存监控动态链接库(DLL,Dynamic Link Library),从而hook(挂钩)内存分配函数和/或windows系统API(Application Programming Interface,应用程序编程接口)。

挂钩模块1104用于通过该内存监控动态链接库挂钩内存分配函数或windows系统应用程序编程接口。

具体地,在内存监控动态链接库中利用微软提供的detours库来实现hook(挂钩)C/C++标准库中的内存分配函数(malloc、free和realloc等)的功能。或者,hook windows系统应用程序编程接口,即hook更底层的HeapAlloc系列函数(包括HeapAlloc、HeapFree和HeapRealloc)或hook底层的系统函数(RtAllocateHeap等)。HeapAlloc函数用来在指定的堆上分配内存,并且分配后的内存不可移动。采用hook C/C++标准库中的内存分配函数,能大量去掉windows系统函数的影响,提高结果的精确性。

此外,还可同时在hook内存分配函数和windows系统应用程序编程接口之间切换。

收集模块1106用于通过挂钩函数调用该内存分配函数或windows系统应用程序编程接口,收集被测进程启动时的内存分配信息,以及收集被测进程退出时的内存分配信息。

具体地,内存监控动态链接库主要监控内存中malloc操作、free操作,包括内存分配的大小、内存分配的地址、内存释放的地址、内存分配的函数堆栈等。hook函数调用内存分配函数malloc或free,通过记录malloc操作和free操作,从而收集被测进程启动时的内存分配信息。该内存分配信息可包括内存分 配地址、内存分配大小和内存分配的函数堆栈,或者包括内存分配大小和内存分配的函数堆栈,或者包括内存分配地址和内存分配大小。

比较模块1108用于对该被测进程启动时的内存分配信息及退出时的内存分配信息进行比较得出内存泄漏信息。

具体地,该内存分配信息可包括内存分配地址、内存分配大小和内存分配的函数堆栈,或者包括内存分配大小和内存分配的函数堆栈,或者包括内存分配地址和内存分配大小。

在一个实施例中,比较模块1108可将被测进程启动时内存分配信息中的内存分配地址和退出时的内存分配信息中的内存分配地址进行比较,判断被测进程退出时内存分配信息中比被测进程启动时内存分配信息中多的内存地址,则多的内存地址为内存泄漏地址,再得出内存泄漏大小。

在另一个实施例中,比较模块1108可将被测进程启动时内存分配信息的函数堆栈和退出时的内存分配信息中的函数堆栈进行比较,判断被测进程退出时内存分配信息中比被测进程启动时内存分配信息中多的函数堆栈,多的函数堆栈为函数堆栈内存泄漏。

图12为另一个实施例中内存泄露检测装置的结构框图。如图12所示,一种内存泄漏检测装置,除了包括加载模块1102、挂钩模块1104、收集模块1106、比较模块1108,还包括第一统计模块1110。其中:

第一统计模块1110用于对多的函数堆栈按照对应的函数地址大小进行排序,再对排序后的函数堆栈进行分类统计,得出函数堆栈内存泄漏次数和函数内存泄漏大小。

具体地,将函数堆栈进行分类,统计同一类的函数堆栈的泄漏次数,并计算出所有函数堆栈内存泄漏大小。

采用函数堆栈比较实际是一组数的比较,只有组内每一个数都相等才算相同的函数堆栈,判断两个函数堆栈是否相等需要每个数都比较,大量堆栈查找的时候比较耗时,采用给函数堆栈排序,可将函数堆栈按照函数地址从大到小排序,或从小到大排序。排序处理后的函数堆栈通过使用std::set数据结构实现红黑树管理,使查找时间复杂度降为O(lgn),极大地缩短了查找时间。

此外,还可统计出内存泄漏的代码行、严重程度、内存分配的函数堆栈的变化趋势等,通过函数堆栈的变化趋势更好的定位内存泄漏问题。

上述内存泄漏检测装置,通过在被测进程启动时加载内存监控动态链接库,通过内存监控动态链接库挂钩内存分配函数,收集被测进程启动时的内存分配信息和退出后的内存分配信息,通过比较被测进程启动时和退出时内存分配信息,得出内存泄漏信息,监控了被测进程从启动开始到退出的整个过程中的内存分配信息,监控的时间点精确,得到的数据全面,且统计出来的结果是始终没有释放的,则得出的内存泄漏信息更加准确。

在一个实施例中,收集模块1106还用于通过挂钩函数调用该内存分配函数和/或windows系统应用程序编程接口,收集被测进程启动后的内存监控信息。

图13为另一个实施例中内存泄露检测装置的结构框图。如图13所示,一种内存泄漏检测装置,除了包括加载模块1102、挂钩模块1104、收集模块1106、比较模块1108,还包括传输模块1112、读取模块1114、存储模块1116、取出模块1118、分配模块1120、刷新模块1122、查找模块1124、删除模块1126。

其中:传输模块1112用于将该内存监控信息发送给数据处理进程,由该数据处理进程对该内存监控信息进行处理。

传输模块1112还用于将该内存监控信息写入共享内存中。

读取模块1114用于通过该数据处理进程的监控线程读取该共享内存中的内存监控信息。

存储模块1116用于将读取的内存监控信息存入该数据处理进程的缓存中。

取出模块1118用于通过该数据处理进程的分配线程从该缓存中取出内存监控信息。

分配模块1120用于分配给该数据处理进程的处理线程进行处理。

刷新模块1122用于通过该数据处理进程的刷新线程对该被测进程的内存监控信息处理得到的内存分配信息进行刷新。

该内存监控信息为内存分配信息或内存释放信息;

查找模块1124用于当该内存监控信息为内存释放信息时,根据该内存释放 信息从该数据处理进程的高速缓存中查找是否存在对应的内存分配信息。

删除模块1126用于若该数据处理进程的高速缓存中存在对应的内存分配信息,则删除该内存释放信息及对应的内存分配信息。

以游戏程序中,玩家从一个场景进入另一个场景,在另一个场景结束后重返原来的场景,其内存泄漏检测包括:

收集模块1106还用于在进入一次被测场景后退出到前序场景开始收集第一内存快照,该第一内存快照记录当前时刻的内存分配信息,以及在再次进入到被测场景后退出到前序场景开始收集第二内存快照,该第二内存快照记录当前时刻的内存分配信息;

比较模块1108还用于将第一内存快照记录的内存分配信息及第二内存快照记录的内存分配信息进行比较,得出被测场景的内存泄漏信息。

或者,收集模块1106还用于在游戏程序启动时收集第一内存快照,该第一内存快照记录当前时刻的内存分配信息,以及在游戏程序退出时收集第二内存快照,该第二内存快照记录当前时刻的内存分配信息;

比较模块1108还用于将第一内存快照记录的内存分配信息及第二内存快照记录的内存分配信息进行比较,得出被测场景的内存泄漏信息。

该内存分配信息包括内存分配未释放的地址、内存分配未释放的大小和内存分配未释放的函数堆栈;

比较模块1108还用于将该第一内存快照记录的内存分配信息中的函数堆栈和第二内存快照记录的内存分配信息中的函数堆栈进行比较,判断第二内存快照记录的内存分配信息中比第一内存快照记录的内存分配信息中多的函数堆栈,则多的函数堆栈为函数堆栈内存泄漏。

图14为另一个实施例中内存泄露检测装置的结构框图。如图14所示,一种内存泄漏检测装置,除了包括加载模块1102、挂钩模块1104、收集模块1106、比较模块1108,还包括第二统计模块1128。

第二统计模块1128用于对多的函数堆栈按照对应的函数地址大小进行排序,再对排序后的函数堆栈进行分类统计,得出函数堆栈内存泄漏次数和函数内存泄漏大小。

具体地,将函数堆栈进行分类,统计同一类的函数堆栈的泄漏次数,并计算出所有函数堆栈内存泄漏大小。

在其他实施例中,内存泄露装置可包括加载模块1102、挂钩模块1104、收集模块1106、比较模块1108、第一统计模块1110、传输模块1112、读取模块1114、存储模块1116、取出模块1118、分配模块1120、刷新模块1122、查找模块1124、删除模块1126、第二统计模块1128任意可能的组合。

本领域普通技术人员可以理解实现上述实施例方法中的全部或部分流程,是可以通过计算机程序来指令相关的硬件来完成,所述的程序可存储于一非易失性计算机可读取存储介质中,该程序在执行时,可包括如上述各方法的实施例的流程。其中,所述的存储介质可为磁碟、光盘、只读存储记忆体(Read-Only Memory,ROM)等。

以上所述实施例仅表达了本发明的几种实施方式,其描述较为具体和详细,但并不能因此而理解为对本发明专利范围的限制。应当指出的是,对于本领域的普通技术人员来说,在不脱离本发明构思的前提下,还可以做出若干变形和改进,这些都属于本发明的保护范围。因此,本发明专利的保护范围应以所附权利要求为准。

当前第1页1 2 3 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1