【C++】内存泄漏分析实战

632 阅读2分钟

我正在参加「掘金·启航计划」

👀持续更新中……

[toc]

前言

本文为项目中服务程序的内存泄漏,挑了其中一部分比较常见的内存泄漏进行分析和修改。

关于内存泄漏和分析的文章可查看博客相关文章,有 UMDHvld 两种方式,可根据个人需求展开分析。

泄漏分类

# malloc 内存泄漏

下述日志为一次 malloc 未正确释放导致的内存泄漏。这种问题比较常见,不用多说。

    ntdll.dll!RtlAllocateHeap()
    ***\heap\malloc_base.cpp (34): XXX.dll!_malloc_base() + 0xF bytes
    XXX.dll!_event_debugx() + 0x1A7 bytes

解决方案:

根据提示的位置找到并释放该内存。

char *ch = (char*)malloc(sizeof(8));
free ch;
ch = NULL;

另外,也可以使用智能指针管理该指针:

char *ch = (char*)malloc(sizeof(8));
shared_ptr<char> spCh(ch);

# new 内存泄漏

关于 new 的内存泄漏分析写了一段代码用来展示一下:

注:代码 Section2 部分为一个崩溃代码,堆内存申请后使用时一处,而在堆内存申请和释放时以及程序结束时释放堆内存时,都 存在堆溢出检查 ,检查点会抛出异常,导致程序奔溃。

// 代码 Section2
// 该函数代码还涉及奔溃问题
int* test_heap_alloc()
{
	int* pTable = new int(256); // 申请一个int结构体变量,初始值为256;
	for (int i = 0; i < 256; i++)
		pTable[i] = i;
	return pTable;
}

int main ()
{
   	test_heap_alloc();
    // 执行上述代码之后再去下边这句申请堆内存会奔溃。
    char* p = new char(0); // 申请一个字节,内容初始化为0
	return 0;
}

以下为 vld 生成的泄漏日志:

WARNING: Visual Leak Detector detected memory leaks!
---------- Block 1 at 0x010AAC68: 4 bytes ----------
  Leak Hash: 0x03729B3D, Count: 1, Total 4 bytes
  Call Stack (TID 33760):
    ucrtbase.dll!malloc()
    d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\heap\new_scalar.cpp (35): vldforptr.exe!operator new() + 0x8 bytes
    d:\code_source\c++\vs2019\vldforptr\vldforptr.cpp (45): vldforptr.exe!main() + 0x7 bytes
    KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes
    ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0x11E bytes
    ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xEE bytes
  Data:
    00 00 00 00                                                  ........ ........

解决方案:

调用 delete 释放 new 出来的内存。

  1. 问题:不确定变量什么时候使用完的,如何释放?

    // 使用智能指针
    char *ch = new char[256];
    std::shared_ptr<char> spCh(ch);
    

# strdup 内存泄漏

strdup函数说明:

功能:将字符串拷贝到新建的位置处

返回值 :返回一个指向新字符串的指针,该字符串是字符串s的副本,如果分配空间失败,则返回NULL值。新字符串的内存由strdup函数原型内部的malloc()获得,需用free()释放。

ntdll.dll!RtlAllocateHeap()
minkernel\crts\ucrt\src\appcrt\heap\malloc_base.cpp (34): XXX.dll!_malloc_base() + 0xF bytes
XXX.dll!BUF_strndup() + 0x8A bytes

把返回内存地址的释放权交给了别的变量,这就很容易忘记释放,所以 strdup 这个函数也很容易造成内存泄漏。

整理

# _malloc_base

_malloc_base 就是编译器编译之后的 malloc

查看vld的log可以看到,堆栈显示的 _malloc_base 所在的文件夹为 minkernel\crts\ucrt\src\appcrt\heap\malloc_base.cpp,这个文件就是 malloc 声明和定义的文件。