Linux 内存泄露检测技巧

由于 C 和 C++ 程序中完全由程序员自主申请和释放内存,稍不注意,就会在系统中导入内存错误。同时,内存错误往往非常严重,一般会带来诸如系统崩溃,内存耗尽这样严重的后果。无论何时何地发生内存泄漏,都可能表现为应用程序完全无法接受,同时内存泄漏不是显而易见[1]。存在内存错误的 C 和 C++ 程序会导致各种问题。如果它们泄漏内存,则运行速度会逐渐变慢,并最终停止运行;如果覆盖内存,则会变得非常脆弱,很容易受到恶意用户的攻击。

什么是内存泄露

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

如何发现内存泄漏
有些简单的内存泄漏问题可以从在代码的检查阶段确定。还有些泄漏比较严重的,即在很短的时间内导致程序或系统崩溃,或者系统报告没有足够内存,也比较容易发现。最困难的就是泄漏比较缓慢,需要观测几天、几周甚至几个月才能看到明显异常现象。那么如何在比较短的时间内检测出有没有潜在的内存泄漏问题呢?实际上不同的系统都带有内存监视工具,我们可以从监视工具收集一段时间内的堆栈内存信息,观测增长趋势,来确定是否有内存泄漏。在 Linux 平台可以用 ps 命令,来监视内存的使用,比如下面的命令 (观测指定进程的VSZ值):


Linux 内存泄露检测工具(一):mtrace
mtrace是三款工具之中是最简单易用的,mtrace是一个C函數,在<mcheck.h>里声明及定义,函数原型为:
    void mtrace(void);
其实mtrace是类似malloc_hook的 malloc handler,只不过mtrace的handler function已由系统为你写好,但既然如此,系统又怎么知道你想将malloc/free的记录写在哪里呢?为此,调用mtrace()前要先设置 MALLOC_TRACE环境变量:
    #include <stdlib.h>
    ....
    setenv("MALLOC_TRACE", "output_file_name", 1);
    ...

output_file_name就是储存检测结果的文件的名称。


但是检测结果的格式是一般人无法理解的,而只要有安装mtrace的话,就会有一名为mtrace的Perl script,在shell输入以下指令:
    mtrace [binary] output_file_name
就会将output_file_name的內容转化成能被理解的语句,例如「No memory leaks」,「0x12345678 Free 10 was never alloc」诸如此类。

例如以下有一函数:(暂且放下single entry single exit的原则)
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <mcheck.h>
    int main() {
        char *hello; 
        setenv("MALLOC_TRACE", "output", 1);
        mtrace();
        if ((hello = (char *) malloc(sizeof(char))) == NULL) {
            perror("Cannot allocate memory.");
            return -1;
        }
        return 0;
    }


执行后,再用mtrace 将结果输出:
    - 0x08049670 Free 3 was never alloc'd 0x42029acc
    - 0x080496f0 Free 4 was never alloc'd 0x420dc9e9
    - 0x08049708 Free 5 was never alloc'd 0x420dc9f1
    - 0x08049628 Free 6 was never alloc'd 0x42113a22
    - 0x08049640 Free 7 was never alloc'd 0x42113a52
    - 0x08049658 Free 8 was never alloc'd 0x42113a96

    Memory not freed:
    -----------------
       Address     Size     Caller
    0x08049a90      0x1  at 0x80483fe

最后一行标明有一个大小为1 byte的内存尚未释放,大概是指「hello」吧。
    若我们把该段内存释放:
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <mcheck.h>
    int main() {
        char *hello;
  
        setenv("MALLOC_TRACE", "output", 1);
        mtrace();
        if ((hello = (char *) malloc(sizeof(char))) == NULL) {
            perror("Cannot allocate memory.");
            return -1;
        }


        free(hello);
        return 0;
    }

结果如下:
    - 0x080496b0 Free 4 was never alloc'd 0x42029acc
    - 0x08049730 Free 5 was never alloc'd 0x420dc9e9
    - 0x08049748 Free 6 was never alloc'd 0x420dc9f1
    - 0x08049668 Free 7 was never alloc'd 0x42113a22
    - 0x08049680 Free 8 was never alloc'd 0x42113a52
    - 0x08049698 Free 9 was never alloc'd 0x42113a96
    No memory leaks.

mtrace的原理是记录每一对malloc-free的执行,若每一个malloc都有相应的free,则代表没有内存泄露,对于任何非malloc/free情況下所发生的内存泄露问题,mtrace并不能找出来。



Linux 内存泄露检测工具(二):memwatch
在三种检测工具当中,设置最简单的算是memwatch,和dmalloc一样,它能检测未释放的内存、同一段内存被释放多次、位址存取错误及不当使用未分配之内存区域。

如何下载:

下载地址1:http://www.hnlyyk.icoc.cc/col.jsp?id=103  点击打开链接 最新版本memwatch-2.71.tar.gz

下载地址2:http://www.linkdata.se/downloads/sourcecode/memwatch/memwatch-2.71.tar.gz

很幸运地,memwatch根本是不需要安装的,因为它只是一组C程序代码,只要在你程序中加入memwatch.h,编译时加上-DMEMWATCH -DMW_STDIO及memwatch.c就能使用memwatch,例如:
gcc -DMEMWATCH -DMW_STDIO test.c memwatch.c -o test

memwatch输出結果
memwatch 的输出文件名称为memwatch.log,而且在程序执行期间,所有错误提示都会显示在stdout上,如果memwatch未能写入以上文件,它会尝试写入memwatchNN.log,而NN介于01至99之间,若它仍未能写入memwatchNN.log,则会放弃写入文件。

我们引用第一篇(mtrace)中所使用过的有问题的代码:
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <memwatch.h>
    int main() {
        char *hello;


        setenv("MALLOC_TRACE", "output", 1);
        mtrace();
        if ((hello = (char *) malloc(sizeof(char))) == NULL) {
            perror("Cannot allocate memory.");
            return -1;
        }
        return 0;
    }


然后在shell中输入以下编译指令:
    gcc -DMEMWATCH -DMW_STDIO test.c memwatch.c -o test


memwatch.log的內容如下:
    ============= MEMWATCH 2.71 Copyright (C) 1992-1999 Johan Lindh =============

    Started at Sat Jun 26 22:48:47 2004

    Modes: __STDC__ 32-bit mwDWORD==(unsigned long)
    mwROUNDALLOC==4 sizeof(mwData)==32 mwDataSize==32

    Stopped at Sat Jun 26 22:48:47 2004

        unfreed: <1> test.c(9), 1 bytes at 0x805108c    {FE .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .}

    Memory usage statistics (global):
     N)umber of allocations made: 1
     L)argest memory usage      : 1
     T)otal of all alloc() calls: 1
     U)nfreed bytes totals      : 1

文件指出,在test.c被执行到第9行时所分配的内存仍未被释放,该段内存的大小为1 byte。


Memwatch使用注意
Memwatch 的优点是无需特別配置,不需安装便能使用,但缺点是它会拖慢程序的运行速度,尤其是释放内存时它会作大量检查。但它比mtrace和dmalloc多了一项功能,就是能模拟系统内存不足的情況,使用者只需用mwLimit(long num_of_byte)函数来限制程式的heap memory大小(以byte单位)。
最详细的使用说明(包括优点缺点,运行原理等)已在README中列出,本人强烈建议各位读者参考该文件。


  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值