内存泄漏检测程序算法优化_第1页
内存泄漏检测程序算法优化_第2页
内存泄漏检测程序算法优化_第3页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、内存泄漏检测程序的算法优化内存泄漏检测动态链接库(ResLeak ),是白箱测试部门为了检查达梦服务器是否存在内存泄漏的情 况而专门开发的。通过在达梦服务器代码中使用该动态链接库,可以很方便的检查岀存在内存泄漏的情况,并能保存发生内存泄漏的堆栈信息,方便开发人员定位存在问题的代码。1、工作原理内存泄漏检测动态链接库的工作原理,基于达梦服务器统一的内存管理架构:达梦服务器的内存管理系统有利于控制使用的内存总量,及加快内存释放和申请的速度。系统对不同的内存需求采用不同的方式,如系统缓冲区,由于在系统运行时大小不会改变,所以就直接利用OS的malloc/free调用,对大量的动态申请/释放, 则采用

2、标准的“Buddy System模式来管理内存池。对于前一 种方式,统一调用函数void* os_malloc(ulint n_size)从操作系统中分配指定大小的内存块,调用函数void* os_free(void* p)释放指定首地址的内存块;对于后一种方式,系统首先通过前一种方式申请一块固定大小的内存池,单独对此共享内存池进行管理,达梦服务器退岀时再调用os_free释放掉整个内存池,其他模块可以直接从该共享内存池上分配内存,统一调用函数void* mem_malloc(ulint size_in_bytes)从共享内存池分配指定大小的内存块,调用函数void mem_free(void

3、* p)释放指定首地址的内存块,注意,这里释放内存并不是把该内存块返回给操作系统,而是返回给达梦 的内存管理系统。由于达梦服务器的所有内存分配和释放的操作都统一在上面这四个函数中,这样就可以简单的修改这 四个函数,在分配一块内存成功之后,保存其首地址、大小、堆栈跟踪等信息,在释放某一内存块时,删 除其相关的保存信息;最后,在达梦服务器正常退岀时,如果存在内存泄漏的情况,则把没有正常释放的 内存块相关的保存信息打印岀来,便于开发人员进行代码分析。2、调用接口和数据结构内存泄漏检测动态链接库提供的头文件resleak.h定义了所申请内存块的相关信息结构:typedef struct leak_st

4、ruct leak_t;struct leak_structvoid* p;/要跟踪的指针char* callstackMAX_LEVEL;/堆栈跟踪信息,MAX_LEVEL宏定义为10int level;/堆栈深度chartime128;/内存块申请时间unsigned intsize;/内存块申请大小unsigned intthrdid;当前线程IDinttype;/ 申请方式,区分 os_malloc 和 mem_mallocintnChanged;/linux下用于堆栈跟踪信息转换的标志struct leak_struct* next;;该数据结构定义的变量用于保存达梦服务器申请内存时

5、的相关信息,并用一个单向链表串联起来。该 单向链表的起始长度为1,在调用初始化接口 leak_init函数时创建了一个头结点,当达梦服务器申请新的 内存时,则创建新的信息结点,插入到该链表中,而在达梦服务器释放内存时,则遍历该链表,寻找到对 应的信息结点后,将其从该链表中删除。头文件resleak.h中还申明了动态链接库的调用接口,如下:1)初始化函数接口:leak_t*leak_init();接口功能:创建信息结点链表的头结点,和多线程互斥变量,并返回头结点地址。2)退岀清理函数接口void leak_deinit(leak_t* head);接口功能:释放链表中所有信息结点和互斥变量3)

6、添加信息结点BOOLadd_leak_point(leak_t* head, void* p,unsigned int nSize,int nType);接口功能:申请内存成功后,向信息结点链表中添加该内存块相关的信息结点。4) 删除信息结点BOOLrelease_leak_point(leak_t* head, void* p,int nType);接口功能:释放内存成功后,在信息结点链表中删除该内存块相关的信息结点。5) 保存未释放信息结点的堆栈信息到文件中BOOLwrite_leak_to_file(leak_t* head, char* filename);接口功能:遍历信息结点链表,

7、打印当前未释放的每一个内存块的堆栈跟踪信息。3、优化原因内存泄漏检测动态链接库(ResLeak )的设计目标之一:不仅可以在Windows环境下使用,也可以在Linux环境下使用,而且对达梦服务器的性能影响很低。在Windows环境下,通过调用 Windows的库函数,可以方便快速的获取堆栈跟踪信息,达梦服务器调用该库带来的性能影响可以忽略不计。在Linux环境下,达梦服务器申请内存成功后,在内存泄漏检测动态链接库的add_leak_point接口中通过调用backtrace函数和backtrace_symbols 函数来获取堆栈跟踪信息,获取的每一层堆栈信息格式如下所示:./应用程序名函数地

8、址具体的例子如下:./dmServer 0x804869c也就是说获取的只是堆栈跟踪函数的内存地址,并不是我们想要的具体函数名称和源代码定位行数。 要想获取这些信息,还必须执行终端命令来进行转换:addr2line -f 0x804869c -e dmServer > filenamel该命令把指定应用程序对应内存地址的具体函数信息保存到filenamel文件中,保存信息的格式如下例所示:os_malloc/root/src/pub/err.c:69filenamel文件中的第一行保存的是具体的函数名称,第二行是源代码的定位行数。因此,在Linux环境下获取堆栈跟踪信息的步骤应该包括以下

9、几步:1) 调用backtrace函数和backtrace_symbols 函数获取堆栈地址信息;2) 从每一层的堆栈地址信息中解析岀函数的内存地址;3) 调用system函数执行addr2line命令,把内存地址转换成具体的函数信息,保存到临时文件中;4) 读取临时文件,从中解析岀函数名称和源代码定位信息,保存到信息结点中。具体的代码如下:void* trace MAX_LEVEL ;char* messages = NULL;int trace_size = 0;int i=0;trace_size = backtrace( trace, MAX_LEVEL );messages = ba

10、cktrace_symbols( trace, trace_size );/ 获取堆栈地址信息for( i = 0; i < trace_size && i< MAX_LEVEL; i+ )p_leak1->callstack i = (void *)malloc(strlen(messagesi)+1);sprintf(p_leak1->callstack i,"%s",messagesi);/保存中间结果-函数地址信息getfunc(p_leak1->callstack i);/ 执行getfunc子函数转换成具体函数信息p

11、_leak1->level = i;采用此逻辑实现内存泄漏检测动态链接库(ResLeak )后,在Linux环境下进行调用测试,发现达梦服务器在启动阶段由于频繁分配内存导致大量调用add_leak_point接口来向信息结点链表中插入新的结点,启动速度极慢。在 Windows环境下正常启动只需要一分钟左右,而在Linux环境下,却需要一小时甚 至更多时间,程序性能极其低下。因此,必须想办法对程序算法进行优化处理,否则内存泄漏检测动态链 接库就无法在Linux下正常使用。4、优化方法在对程序进行优化前,首先要了解该程序的性能瓶颈所在之处。通过分析,发现获取堆栈跟踪信息的第3步调用syste

12、m函数执行addr2line命令的时间开销最大。但是,这一步是把函数内存地址转换成函数具体信息的必由之路,目前在 Linux环境下还没有其他替代方案。既然无法避免,那么如何提高这一 步的效率也就成了优化程序性能的关键。优化方式一:并行处理通过分析,我们发现,在第1步中调用backtrace函数和backtrace_symbols函数可以一次获取多层堆栈地址信息,而每一层堆栈地址信息的转换处理可以互不干扰。因此,可以考虑用多线程方式来同时处 理每一层堆栈信息,需要注意的一点是,每个线程都需要使用单独的文件来保存转换后的信息。于是修改 代码如下:pthread_t pidMAX_LEVEL; /定

13、义多线程对象for( i = 0; i < trace_size && i< MAX_LEVEL; i+ )p_leak1->callstack i = (void *)malloc(strlen(messagesi)+1);sprintf(p_leak1->callstack i,"%s",messagesi);/并发执行getfunc子函数来获取具体函数信息if(pthread_create(&pidi,NULL,getfunc,(void *)&(p_leak1->callstacki)!=0)break;

14、p_leak1->level = i;for( i = 0; ilevel; i+ )pthreadoin(pidi,NULL);/ 等待每一层的转换处理线程结束第3步中调用system函数执行addr2line命令来保存具体函数信息到文件中,每个线程使用的文件都不能冲突,因此可以最多用10个文件来同时进行转换操作。通过并行处理,理论上最高可以提高程序 10倍的性能。修改后进行调用测试,发现程序启动速度得到 有效的提高,但是仍然需要2030分钟的时间,其性能依然达不到我们的需求。优化方式二:延后处理通过分析,进一步发现,达梦服务器申请内存时插入的很多信息结点,在释放的时候又会删除掉,这

15、些结点都进行了转换操作。而之所以要进行堆栈地址信息的转换,仅仅只是为了保存未释放信息结点的详 细堆栈信息到文件中,对于那些保存之前已经删除的信息结点,进行的转换过程都是多余的操作。因此, 可以在申请内存时调用的add_leak_point接口中把转换过程的24步省略掉,只在达梦服务器调用write_leak_to_file函数接口保存当前未释放信息结点的堆栈信息到文件中时,才对信息结点中的中间结果 进行转换,每个信息结点转后之后都将nChanged标识置为1,避免下一次保存时重复操作,这样就可以尽量过滤掉那些中途删掉的信息结点的转换开销。修改程序,把转换过程的24步从内存泄漏检测动态链接库的a

16、dd_leak_point函数接口转移到write_leak_to_file 函数接口中,修改后代码如下:if(!p_leak1->nChanged)pthread_t pidMAX_LEVEL;for(i=O;ilevel;i+)if(pthread_create(&pidi,NULL,getfunc,(void *)&(p_leak1->callstacki)!=0) break;elsep_leak1->nChanged = 1; /设置已转换标志for( i = 0; ilevel; i+ )pthread _join(pidi,NULL);通过这次修

17、改,在达梦服务器没有调用保存函数接口之前,所有的转换过程都省略掉,有效提高了达梦服务器的启动速度和执行速度,基本做到与Windows环境下的性能相当。唯一美中不足的地方在于:在达梦服务器运行期间,手工输入命令调用write_leak_to_file函数接口保存信息结点到文件时,第一次调用过程中转换的时间要花费10分钟左右,后续的调用时间虽然明显减少,但仍然不能满足我们使用要求。优化方式三:以空间换取时间最后,仔细分析一下从函数内存地址转换到函数名称和源代码定位行数的过程,我们可以发现,虽然 达梦服务器在执行的过程中分配内存的次数可能非常巨大 这也意味着前面的程序进行堆栈地址信息转 换的次数也非

18、常巨大,但是,达梦服务器的函数总个数是非常有限的,也即函数内存地址的总数量是非常 有限的。那么,我们完全可以在内存中建立一个函数内存地址与具体函数信息的映射链表,这样就不需要 每次都调用效率低下的 system 函数来执行 addr2line 转换命令,而是直接在映射链表中寻找已有结果,只 有在映射链表中不存在对应的函数内存地址记录时,才进行此操作,并把结果保存到映射链表中,处理速 度将得到明显提高。实现的流程如下:1、达梦服务器调用 write_leak_to_file 函数接口来保存信息,遍历每一个信息结点;2、如果当前结点的堆栈地址信息已经转换过,则转到第5 步;3、如果当前结点的堆栈地址信息没有经过转换,则解析每一层的函数内存地址;4、如果函数内存地址在映射链表中不存在,则调用system 函数来执行 addr2line 转换,并把转换结果保存到映射链表中,避免下次重复转换;如果映射链表中存在对应地址,则直接取相关的函数名称和源 代码定位行数;5、把转换后的信息结点保存到文件中。修改后重新进行测

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论