天天看点

内存泄漏工具

学习文章,原文:https://blog.csdn.net/qq_23350817/article/details/90641856

概述

内存泄漏(memory leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,在大型的、复杂的应用程序中,内存泄漏是常见的问题。当以前分配的一片内存不再需要使用或无法访问时,但是却并没有释放它,这时就出现了内存泄漏。尽管优秀的编程实践可以确保最少的泄漏,但是根据经验,当使用大量的函数对相同的内存块进行处理时,很可能会出现内存泄漏。

内存泄露分类

1. 常发性内存泄漏。

发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。

2. 偶发性内存泄漏。

发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。

3. 一次性内存泄漏。

发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅一块内存发生泄漏。比如,在一个Singleton类的构造函数中分配内存,在析构函数中却没有释放该内存。而Singleton类只存在一个实例,所以内存泄漏只会发生一次。

4. 隐式内存泄漏。

程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

检测工具

现在有很多方法来检测内存泄露,以下列举了常用的内存泄露检测工具。

1、Visual Leak Detecter

应用环境: Windows + VC

编程语言: C/C++

使用方法: 只需包含头文件vld.h,并添加提供的lib

结果输出: 输出到VC的调试窗口中

设计思路: 注册_CrtSetAllocHook钩子函数,使用VC自带的CRT Debug Heap

优缺点: 可以获得内存泄露点的调用堆栈,可以得到内存泄露的完整数据

如何获取: http://www.codeproject.com/Articles/9815/Visual-Leak-Detector-Enhanced-Memory-Leak-Detectio

2、Bounds Checker

应用环境: Windows + VC6.0

编程语言: C/C++

使用方法: 安装使用,会自动在VC内创建右键菜单

结果输出: 输出到VC的调试窗口中

设计思路: 未知

优缺点: 可以检测内存泄露;资源泄漏;对指针的错误操作,内存读、写溢出;使用未初始化的内存。

如何获取: http://3ddown.com/soft/31594.html

安装licence时,需要将日期调整为2008年,然后安装licence。有一个licence安装后显示是8.3的,但是可以使用。

3、mtrace

应用环境: Linux GLIBC

编程语言: C

使用方法: 包含头文件mcheck.h,定义环境变量MALLOC_TRACE为输出文件名,程序开始时调用mtrace()即可。

结果输出: 用户指定的文件

设计思路: 为malloc,realloc,free函数添加钩子函数,记录每一对malloc-free的执行

优缺点: 只能检查使用malloc/realloc/free造成的的内存泄露

如何获取: GLIBC自带,可直接使用

使用方法:https://blog.csdn.net/weixin_42887343/article/details/113642463

4、memwatch

应用环境: Linux

编程语言: C

使用方法: 加入memwatch.h,编译时加上-DMEMWATCH -DMW_STDIO及memwatch.c

结果输出: 输出文件名称为memwatch.log,在程序执行期间,错误提示都会显示在stdout上。

设计思路: 将malloc/realloc/calloc/strdup/free等重定义为mwMalloc(sz, FILE, LINE)等,内部维护一个操作链表。

优缺点: 能检测双重释放(double-free)、错误释放(erroneous free)、内存泄漏(unfreed memory)、溢出(Overflow)、下溢(Underflow)等等

如何获取: http://memwatch.sourceforge.net/

5、valgrind

应用环境: Linux

编程语言: C/C++

使用方法: 加入memwatch.h,编译时加上-DMEMWATCH -DMW_STDIO及memwatch.c

结果输出: 输出文件名称为memwatch.log,在程序执行期间,错误提示都会显示在stdout上

设计思路: 根据软件的内存操作维护一个有效地址空间表和无效地址空间表(进程的地址空间)

优缺点:

能够检测:

使用未初始化的内存 (Use of uninitialised memory)

使用已经释放了的内存 (Reading/writing memory after it has been free’d)

使用超过 malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)

对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)

申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)

如何获取: http://valgrind.org/

使用方法: 如果是QT代码,可以直接配合qtCreator使用,使用方法:https://blog.csdn.net/weixin_42887343/article/details/113655059

6、debug_new

应用环境: Linux/Windows

编程语言: C++

使用方法: 包含头文件debug_new.h,链接debug_new.cpp

结果输出: 控制台console

设计思路: 通过重载new和delete操作符来捕获内存申请/释放请求,并在程序内部维护一个全局静态变量的哈希链表。在new操作符中,不仅仅分配用户所要求的内存,而是在为每次分配的内存都添加一个头部,存储着此次分配的位置信息和链表指针,new返回的是分配的这块内存加上头部偏移后的值,而在之前已经将此返回值作了HASH计算并添加到HASH链表中了。delete的时候先根据要释放的指针地址做HASH计算,然后再遍历数组HASH值处的链表进行查找,如果找到则将该节点移除,未找到就abort。这样在程序结束之后,通过检查此数组中是否还有未释放的内存块来确定是否有内存泄露。

优缺点: 跨平台,仅用于C++程序,

如何获取: http://www.ibm.com/developerworks/cn/linux/l-mleak2/index.html

总结

以上的这些分析工具,所使用的方法大致分为以下几种:

  1. 注册内存分配/释放钩子函数(hook)。在Linux下可以malloc_hook,

    free_hook等5个钩子函数,在Windows下可以注册_CrtSetAllocHook钩子函数,这样在分配内存的时候就可以捕获这一请求并加以处理。Visual

    Leak Detecter和mtrace使用此方式。

  2. 使用宏定义替换。将用户代码中的malloc, free 替换为宏定义的 mwMalloc(sz, FILE,

    LINE)等自定义函数,从而跟踪内存请求,memwatch即使用此方式。

  3. 操作符重载。此方法仅用于C++语言中,通过重载new、delete操作符来实现跟踪内存请求,重载后的操作符类似于钩子函数意义。debug_new采用此方式。

这些工具的输出方式也分以下几种:

  1. Windows VC环境下一般输出到调试窗口中,因此VC本身就提供了一个理想的输出场所,并且GUI应用程序输出到标准输出时不可见的。Visual Leak Detecter采用此法。
  2. 输出到标准输出或标准错误输出:控制台应用程序可以输出到屏幕,如memwatch, valgrind,

    debug_new都是采用这种方法。

  3. 输出到日志文件:将结果输出到用户指定或默认的日志文件中,如mtrace和memwatch。

此外,这些工具的内存检测方式无非也分为两种:

  1. 维护一个内存操作链表,当有内存申请操作时,将其加入此链表中,当有释放操作时,从申请操作从链表中移除。如果到程序结束后此链表中还有内容,说明有内存泄露了;如果要释放的内存操作没有在链表中找到对应操作,则说明是释放了多次。使用此方法的有VC内置的调试工具,Visual

    Leak Detecter,mtrace, memwatch, debug_new。

  2. 模拟进程的地址空间。仿照操作系统对进程内存操作的处理,在用户态下维护一个地址空间映射,此方法要求对进程地址空间的处理有较深的理解。因为Windows的进程地址空间分布不是开源的,所以模拟起来很困难,因此只支持Linux。采用此方法的是valgrind。

继续阅读