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

2023年2月20日11:29:24

一、内存泄漏检测工具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!!!

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

  • 作者:njr465167967
  • 原文链接:https://blog.csdn.net/njr465167967/article/details/126342965
    更新时间:2023年2月20日11:29:24 ,共 3333 字。