C++内存泄漏的定位方法总结

一、内存泄漏检测工具Valgrind安装及使用

  • 下载Valgrind工具源码:

    http://www.valgrind.org/downloads/valgrind-3.14.0.tar.bz2

  • 解压缩:

    tar -jxvf valgrind-3.14.0.tar.bz2

  • 进入安装后的目录进行安装:

    cd valgrind-3.14.0

    ./configure --prefix=/home/NJR/valgrind

    make

    make install

  • 配置环境变量:

    vi /etc/profile

    最后一行加入:export PATH=$PATH:/home/NJR/valgrind/bin

    生效环境变量:source /etc/profile

  • 假设想要检测的执行文件是main,并且想把检测结果输入到文件valgrind_report.log中,就执行下面语句:

    valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --run-libc-freeres=yes --log-file=./valgrind_report.log ./test

    如果只想把结果打印到屏幕上,就执行下面语句

    valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --run-libc-freeres=yes ./test

  • 举例子:

    #include <iostream>  void func(void) {     int *x = (int *)malloc(8 * sizeof(int));     x[9] = 0;              //数组下标越界 }                        //内存未释放    int main(void) {     func();      return 0; }

        执行编译命令: 

        gcc -Wall test.cpp -g -fno-inline -o test

运行后的结果,可以看到

==56206== Memcheck, a memory error detector ==56206== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==56206== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info ==56206== Command: ./test ==56206== Parent PID: 40069 ==56206==  ==56206== Invalid write of size 4 ==56206==    at 0x400514: getMemory() (test.cpp:6) ==56206==    by 0x400525: main (test.cpp:12) ==56206==  Address 0x5201064 is 4 bytes after a block of size 32 alloc'd ==56206==    at 0x4C2DE4D: malloc (vg_replace_malloc.c:299) ==56206==    by 0x400507: getMemory() (test.cpp:5) ==56206==    by 0x400525: main (test.cpp:12) ==56206==  ==56206==  ==56206== HEAP SUMMARY: ==56206==     in use at exit: 32 bytes in 1 blocks ==56206==   total heap usage: 1 allocs, 0 frees, 32 bytes allocated ==56206==  ==56206== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==56206==    at 0x4C2DE4D: malloc (vg_replace_malloc.c:299) ==56206==    by 0x400507: getMemory() (test.cpp:5) ==56206==    by 0x400525: main (test.cpp:12) ==56206==  ==56206== LEAK SUMMARY: ==56206==    definitely lost: 32 bytes in 1 blocks ==56206==    indirectly lost: 0 bytes in 0 blocks ==56206==      possibly lost: 0 bytes in 0 blocks ==56206==    still reachable: 0 bytes in 0 blocks ==56206==         suppressed: 0 bytes in 0 blocks ==56206==  ==56206== For counts of detected and suppressed errors, rerun with: -v ==56206== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

二、重载全局或局部类内operator new操作符和delete操作符

原理:

1.定义一个全局的内存信息表,用来存储内存申请的文件名及位置

2.重载operator new/new[],保存内存申请信息

3.重载operator delete/delete[],删除内存申请信息

4.定义一个全局/静态常量,在程序结束时,核查内存信息表是否还存在内存申请信息,如果存在则说明发生内存泄漏,否则无内存泄漏

struct Info {     void *ptr;     const char *file_name;     long line; };  Info ptr_list[1024]; unsigned int ptr_num = 0;  int find_ptr(void *p) {     for (unsigned int i = 0; i < ptr_num; ++i)     {         if (ptr_list[i].ptr == p)         {             return i;         }     }      return -1; }  void del_ptr(unsigned int i) {     while(i+1 < ptr_num)     {         ptr_list[i] = ptr_list[i+1];         i++;     }      ptr_num--; }  struct ProcEnd {     ~ProcEnd()     {         for (unsigned int i = 0; i < ptr_num; ++i)         {             printf("file: %s, line: %d, memory leak!!!\n", ptr_list[i].file_name, ptr_list[i].line);         }     } };  void* operator new(size_t size, const char *file_name, long line) {     printf("global new\n");     void *p = malloc(size);     ptr_list[ptr_num].ptr = p;     ptr_list[ptr_num].file_name = file_name;     ptr_list[ptr_num].line = line;     ptr_num++;     return p; }  void* operator new[](size_t size, const char *file_name, long line) {     return operator new(size, file_name, line); }  void operator delete(void *p) {     int i = find_ptr(p);     if (i != -1)     {         free(p);         del_ptr(i);     }     else     {         printf("delete unknown pointer!!!\n");     } }  void operator delete[](void *p) {     operator delete(p); }  ProcEnd end;  struct BTNode {     BTNode(char val) : val(val), left(nullptr), right(nullptr) {}      char val;     BTNode *left;     BTNode *right; };

检验输出结果:

#include <iostream>  int main() {     int *i = new int(0);     return 0; }

控制台打印信息:

global new
file: ../src/TEST.cpp, line: 197, memory leak!!!

可以看出发生了内存泄漏,并提示对应的文件名称和行号。