感兴趣的欢迎移步到我的知乎:
Windows 平台定位C语言工程内存泄漏 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/443548463
关于如何在 Windows 平台定位C语言工程内存泄漏问题时,如果您时间紧,看这个一分钟版本即可:
用宏重定义 malloc:
#define malloc(c) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
在应用程序初始化开始,添加下面一行代码:
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
调试模式下,进行Debug,程序结束后便会在输出窗口中显示有无内存泄漏,如果存在,便会打印对应的分配内存的文件、行定位,以及泄漏的字节数量。
如下图所示:
好了,问题到此便解决了。
如果您有时间深究了,我们就可以来慢慢聊聊。
开发过程中,检测 C/C++ 下的内存泄漏一般有两类方法:
- 第三方工具类;
- 嵌入到代码中的源码监控类;
两类方法尺有所长,寸有所短。虽然我个人更倾向于嵌入到代码中的源码监控方式,但是毕竟这种方法也有自身的局限性,加上自己之前在 Linux 平台做开发时,内存泄漏检测工具 pprof 曾经也帮了我不少忙。
今天只聊聊 _malloc_dbg 方法。C++ 下的 new/delete 有类似方法,本文不再赘述。
_malloc_dbg 的功能是调试模式下分配内存。
接口含义如下所示:
void * _malloc_dbg(
size_t size, // 内存块的请求大小(以字节为单位);
int blockType, // 内存块的请求类型: _CLIENT_BLOCK 或 _NORMAL_BLOCK;
const char *filename, // 指向请求分配操作的源文件名的指针或 NULL
int linenumber // 请求分配操作所在的源文件中的行号或 NULL
);
_malloc_dbg 是 malloc 函数的调试版本。malloc 和_malloc_dbg 都在基堆中分配内存块,但 _malloc_dbg 提供多种调试功能:用于测试泄漏的块的用户部分两侧的缓冲区、用于跟踪特定分配类型的块类型参数,以及用于确定分配请求的源的文件名与行信息。_malloc_dbg 分配的内存块,其空间比请求的大小稍多。其他空间将由调试堆管理器用于链接调试内存块,以及提供具有调试标头信息的应用程序和覆盖缓冲区。
_CrtSetDbgFlag
用 _CrtSetDbgFlag 检索或修改 _crtDbgFlag 标志的状态,以控制调试堆管理器的分配行为(仅限调试版本)。使用_CrtSetDbgFlag 函数,应用程序通过修改调试堆管理器标志的位字段来控制调试堆管理器_crtDbgFlag分配。 通过设置位(打开),该应用程序可指示调试堆管理器执行特殊的调试操作,包括在应用程序退出时检查内存泄露并报告是否找到任何内存泄露、通过指定已释放的内存块应保留在堆的链接列表中来模拟内存不足情况,以及通过在每次分配请求时检查每个内存块来验证该堆的完整性。 未 _DEBUG 时,预处理 _CrtSetDbgFlag 删除对 _CrtSetDbgFlag 的调用。
下表列出了 _crtDbgFlag 的位域并描述了其行为。 因为设置位将导致诊断输出增加、程序执行速度减慢,因此在默认情况下不会设置这些位(已关闭)。
位域 | 默认 | 描述 |
---|---|---|
_CRTDBG_ALLOC_MEM_DF | ON | 打开:启用调试堆分配和使用内存块类型标识符,例如_CLIENT_BLOCK。 关闭:将新的分配添加到堆链接列表,但是将块类型设置为 _IGNORE_BLOCK。 |
_CRTDBG_CHECK_ALWAYS_DF | OFF | 打开:在每次分配和解除分配请求时调用 _CrtCheckMemory。 OFF:_CrtCheckMemory显式调用 。 |
_CRTDBG_CHECK_CRT_DF | OFF | 打开:在 _CRT_BLOCK 和内存状态差异操作中包括类型。 关闭:这些操作将忽略运行时库在内部使用的内存。 |
_CRTDBG_DELAY_FREE_MEM_DF | OFF | 打开:将已释放的内存块保留在堆链接列表中、向其分配 _FREE_BLOCK 类型,然后使用字节值 0xDD 进行填充。 关闭:不要将已释放的块保留在堆链接列表中。 |
_CRTDBG_LEAK_CHECK_DF | OFF | 打开:在程序退出时通过对 _CrtDumpMemoryLeaks 的调用执行自动泄露检查,如果应用程序无法释放它分配的所有内存,则生成错误报告。 关闭:不要在程序退出时自动执行泄露检查。 |
下面两个链接是微软的官方文档,有兴趣可以看看:
_malloc_dbg:
_CrtSetDbgFlag:
当然也可以将错误消息重定向至自定义的类型,如文件中。可以参考如下函数:
int _CrtSetReportMode(
int reportType,
int reportMode
);
_HFILE _CrtSetReportFile(
int reportType,
_HFILE reportFile
);
实验
实验部分,其实也就是本文开头的一分钟版本,为了文章的完整性,还是再强调一遍具体的执行流程。
用宏重定义 malloc:
#define malloc(c) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
在应用程序初始化开始,添加下面一行代码:
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
调试模式下,进行Debug,程序结束后便会在输出窗口中显示有无内存泄漏,如果存在,便会打印对应的分配内存的文件、行定位,以及泄漏的字节数量。
如下图所示:
优劣势总结
优势 | 1、应用非常方便快捷; 2、定位精确,返回信息量大,如泄漏文件名、行数、大小; |
劣势 | 1、必须有C工程源码,在其中应用; 2、调用其它语言时,无效; |