# Windows C++ 内存泄漏调试技术

(adsbygoogle = window.adsbygoogle || []).push({});

目录

# 内存泄漏特征

先用Process Explorer工具检查目标进程,下载地址: (进程资源管理器 - Sysinternals | Microsoft Learn (opens new window)

观察Private Bytes增长情况

可见内存呈增长趋势说明有内存泄漏

# 内存泄漏静态代码排查方向

  1. 排查项目里面申请内存关键字new、malloc、HeapAlloc、VirtualAlloc、GlobalAlloc等,检查代码上下文,看申请到的内存是否有配对的释放函数。

  2. 排查项目API调用相关代码,关键字如:open、create等字样的API,检查它们是否需要调用对应的close函数。

  3. 排查项目里面的HANDLE、HKEY、FILE等类型,检查它们在生命周期结束后是否正确close。

  4. 排查项目第三方库,项目里面使用第三方库,如JSON、curl、SQLite,检查所用到的API,是否遵循文档指南调用释放内存函数。

# 内存泄漏问题动态排查

安装Windbg指定版本,其他版本可能无法看到堆内存占用排序

需要确定目标进程或者dump是32位还是64位,选取指定平台的windbg,否则可能无法查看调用栈情况。

进入Windbg目录下,找到gflags.exe运行,给目标进程增加userstack trace。

打开:gflags.exe /i <进程名称> +ust 关闭:gflags.exe /i <进程名称> -ust

设置代码和PDB目录,启动调试进程

启动进程

运行一段时间,暂停检查堆分配情况

运行堆检查命令

!heap -s

再go恢复运行一段时间,然后再调用!heap -s查看当前堆尺寸增长

如检查堆地址0e0b0000情况:!heap -stat -h 0e0b0000

检查尺寸为67e80大小的内存所有分配情况 !heap -flt s 67e80

检查这些用户指针是在哪个函数分配的

!heap -p -a 4eb73ce0

如果有PDB可以查看函数对应代码位置,使用u命令,检查指定函数地址代码位置

u xxxDll!xxxxxx::GetRunStatus+0x00000b6e

# 在代码启用运行时库内存泄漏检测

如动态或静态排查无头绪,可以在代码启动运行时库代码检查,配合vs debug调试

[https://docs.microsoft.com/zh-cn/visualstudio/debugger/finding-memory-leaks-using-the-crt-library?view=vs-2022](使用 CRT 库查找内存泄漏 - Visual Studio (Windows) | Microsoft Learn (opens new window))

检测内存泄漏的主要工具是 C/C++ 调试器和 C 运行时库 (CRT) 调试堆函数。 若要启用调试堆的所有函数,在 C++ 程序中,按以下顺序包含以下语句:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

使用上面的语句启用调试堆函数后,在应用出口点之前放置 _CrtDumpMemoryLeaks 以在应用退出时显示内存泄漏报告。

_CrtDumpMemoryLeaks();

完整代码如下:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>

int main()
{
    char* p = (char*)malloc(51);
    _CrtDumpMemoryLeaks();
    return 0;
}

结束后调试窗口输出如下,显示有内存泄漏:

# 句柄泄漏排查

句柄使用完未关闭也会导致内存泄漏

句柄: 在windows程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源时会为他们分配内存,并返回标示这些资源的标示号,即句柄 [1] 。 句柄指的是一个核心对象在某一个进程中的唯一索引,而不是指针。 由于地址空间的限制,句柄所标识的内容对进程是不可见的,只能由操作系统通过进程句柄列表来进行维护。句柄列表:每个进程都要创建一个句柄列表,这些句柄指向各种系统资源,比如信号量,线程,和文件等,进程中的所有线程都可以访问这些资源 [2] 。

用Process Explorer工具检查目标进程:

运行一段时间后再观察,有明显增长

此时说明有句柄泄漏。打开句柄视图

检查对应句柄在代码打开的位置,检查他们在使用完毕后是否被正确关闭。

(adsbygoogle = window.adsbygoogle || []).push({});
Last Updated: 4/30/2023, 9:10:11 PM