内存泄漏检测程序的算法优化

上传人:人*** 文档编号:470151217 上传时间:2022-07-18 格式:DOCX 页数:7 大小:43.83KB
返回 下载 相关 举报
内存泄漏检测程序的算法优化_第1页
第1页 / 共7页
内存泄漏检测程序的算法优化_第2页
第2页 / 共7页
内存泄漏检测程序的算法优化_第3页
第3页 / 共7页
内存泄漏检测程序的算法优化_第4页
第4页 / 共7页
内存泄漏检测程序的算法优化_第5页
第5页 / 共7页
点击查看更多>>
资源描述

《内存泄漏检测程序的算法优化》由会员分享,可在线阅读,更多相关《内存泄漏检测程序的算法优化(7页珍藏版)》请在金锄头文库上搜索。

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

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

4、ak_struct leak_t;struct leak_structvoid* p;/要跟踪的指针char* callstackMAX_LEVEL; /堆栈跟踪信息,MAX_LEVEL 宏定义为 10int level;堆栈深度char time128;内存块申请时间unsigned int size;内存块申请大小unsigned int thrdid;当前线程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);接口功能:释放链表中所有信息

6、结点和互斥变量3) 添加信息结点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 );mess

10、ages = backtrace_symbols( trace, trace_size );获取堆栈地址信息for( i = 0; i trace_size & icallstack i = (void *)malloc(strlen(messagesi)+1);sprintf(p_leak1-callstack i,%s,messagesi); 保存中间结果-函数地址信息getfunc(p_leak1-callstack i);/执行getfunc子函数转换成具体函数信息p leak1-level = i;采用此逻辑实现内存泄漏检测动态链接库(ResLeak )后,在Linux环境下进行调用

11、测试,发现达梦 服务器在启动阶段由于频繁分配内存导致大量调用add_leak_point接口来向信息结点链表中插入新的结 点,启动速度极慢。在Windows环境下正常启动只需要一分钟左右,而在Linux环境下,却需要一小时甚 至更多时间,程序性能极其低下。因此,必须想办法对程序算法进行优化处理,否则内存泄漏检测动态链 接库就无法在 Linux 下正常使用。4、优化方法 在对程序进行优化前,首先要了解该程序的性能瓶颈所在之处。通过分析,发现获取堆栈跟踪信息的第3步调用system函数执行addr2line命令的时间开销最大。但是,这一步是把函数内存地址转换成函数具体信息的必由之路,目前在Linu

12、x环境下还没有其他替代方案。既然无法避免,那么如何提高这一 步的效率也就成了优化程序性能的关键。优化方式一:并行处理通过分析,我们发现,在第1步中调用backtrace函数和backtrace_symbols函数可以一次获取多层 堆栈地址信息,而每一层堆栈地址信息的转换处理可以互不干扰。因此,可以考虑用多线程方式来同时处 理每一层堆栈信息,需要注意的一点是,每个线程都需要使用单独的文件来保存转换后的信息。于是修改 代码如下:pthread_t pidMAX_LEVEL; 定义多线程对象for( i = 0; i trace_size & icallstack i = (void *)mallo

13、c(strlen(messagesi)+1);sprintf(p_leak1-callstack i,%s,messagesi);并发执行getfunc子函数来获取具体函数信息if(pthread_create(&pidi,NULL,getfunc,(void *)&( p_leak1-callstacki)!=0)break;p_leak1-level = i;for( i = 0; ilevel; i+ )pthreadoin(pidi,NULL); 等待每一层的转换处理线程结束第3步中调用system函数执行addr2line命令来保存具体函数信息到文件中,每个线程使用的文件都 不能冲突

14、,因此可以最多用10个文件来同时进行转换操作。通过并行处理,理论上最高可以提高程序10倍的性能。修改后进行调用测试,发现程序启动速度得到 有效的提高,但是仍然需要2030分钟的时间,其性能依然达不到我们的需求。优化方式二:延后处理通过分析,进一步发现,达梦服务器申请内存时插入的很多信息结点,在释放的时候又会删除掉,这 些结点都进行了转换操作。而之所以要进行堆栈地址信息的转换,仅仅只是为了保存未释放信息结点的详 细堆栈信息到文件中,对于那些保存之前已经删除的信息结点,进行的转换过程都是多余的操作。因此, 可以在申请内存时调用的 add_leak_point 接口中把转换过程的 24 步省略掉,只

15、在达梦服务器调用 write_leak_to_file 函数接口保存当前未释放信息结点的堆栈信息到文件中时,才对信息结点中的中间结果 进行转换,每个信息结点转后之后都将 nChanged 标识置为 1,避免下一次保存时重复操作,这样就可以 尽量过滤掉那些中途删掉的信息结点的转换开销。修改程序,把转换过程的24步从内存泄漏检测动态链接库的add_leak_point函数接口转移到 write_leak_to_file 函数接口中,修改后代码如下:通过这次修改,在达梦服务器没有调用保存函数接口之前,所有的转换过程都省略掉,有效提高了达 梦服务器的启动速度和执行速度,基本做到与Windows环境下的性能相当。唯一美中不足的地方在于:在 达梦服务器运行期间,手工输入命令调用 write_leak_to_file 函数接口保存信息结点到文件时,第一次调用 过程中转换的时间要花费10 分钟左右,后续的调用时间虽然明显减少,但仍然不能满足我们使用要求。优

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 建筑/环境 > 建筑资料

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号