内存泄漏调试(1)——CRT

来源:互联网 发布:会员数据分析维度 编辑:程序博客网 时间:2024/06/15 10:04

原创文章,转载请注明出处


目录

      • 目录
    • 内存泄漏
    • C Run-Time Libraries CRT
    • 设置断点


内存泄漏

内存泄漏的简单定义是没有正确的释放已经分配的内存,这在C/C++程序中是一个很难检测的bug;一块较小的内存泄漏可能很容易被忽视,但如果内存泄漏的现象不断积累,后果可能导致程序的崩溃,甚至还会影响到其他程序的运行,所以内存泄漏的问题值得重视。一方面需要有良好的编程习惯,但即使是最优秀的程序员也不能保证所写的代码里没有内存泄漏问题;令一方面使用工具帮助检测以及定位内存泄漏点。

内存泄漏问题的调试一是要检测是否有内存泄漏;二是要定位内存泄漏点。

C Run-Time Libraries (CRT)

为了检测内存泄漏,在CRT中定义了debug heap functions;在需要进行内存调试的代码中添加以下声明(必须按以下顺序):

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

在crtdbg.h中,将C标准的malloc和free库函数映射到_malloc_dbg和free方法,通过映射的方法来记录内存的分配和释放情况;一旦引入了上述声明后,在目标程序的退出位置前通过调用 _CrtDumpMemoryLeaks()就能检测到是否有内存泄漏;但这样的话只能知道是否有内存泄漏,不知道在哪里发生了内存泄漏。

通过以下宏定义,可以在调试内存泄漏时输出发生内存泄漏的文件以及在该文件下的代码行数:

#ifdef _DEBUG       #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)#endif

下面是一个简单的测试用例:

#define _CRTDBG_MAP_ALLOC#include <cstdlib>#include <crtdbg.h>#ifdef _DEBUG       #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)#endifint main() {       int* p_int = new int(1234);       char* p_char = new char('a');       _CrtDumpMemoryLeaks();       return 1;}

如果在程序中有多个退出位置,在每个退出位置调用_CrtDumpMemoryLeaks();显然不那么友好,这时可以通过声明:
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
由此可以实现在每个程序退出的位置都会自动调用_CrtDumpMemoryLeaks();

下面是声明了_CrtSetDbgFlag的测试用例:

#define _CRTDBG_MAP_ALLOC#include <cstdlib>#include <crtdbg.h>inline void enableMemLeakCheck() {       _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);}#ifdef _DEBUG       #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)#endifint main() {       enableMemLeakCheck();       int* p_int = new int(1234);       char* p_char = new char('a');       return 1;}

在Debug下的输出:

Detected memory leaks!
Dumping objects ->
e:\wzcgit\testleak\testleak\test.cpp(12) : {67} normal block at 0x00315F60, 1 bytes long.
Data: 61
e:\wzcgit\testleak\testleak\test.cpp(11) : {66} normal block at 0x00315F30, 4 bytes long.
Data: < > D2 04 00 00
Object dump complete.
线程 0xd80 已退出,返回值为 1 (0x1)。
程序“[6636] testleak.exe”已退出,返回值为 1 (0x1)。

简单剖析输出结果:
“e:\wzcgit\testleak\testleak\test.cpp(12)” ——文件名(行号)。
“{67} normal block at 0x00315F60”——内存分配的块号,我理解的是第67块内存分配时出现内存泄漏问题。
“1 bytes long.”——内存泄漏的大小。
“Data: 61”——内存泄漏的数据。

使用CRT生成的内存泄漏报告中主要会有以下信息:

  1. The memory allocation number, which is 12 in this example
  2. The block type, which is normal in this example.
  3. The hexadecimal memory location, which is 0x00315F60 in this example.
  4. The size of the block, 1 bytes in this example.
  5. The first 16 bytes of data in the block, in hexadecimal form.

总结:上述方法能检测出内存泄漏并知道在哪里出现内存泄漏,但不知道内存泄漏的原因。

设置断点

在函数入口设置断点,VS下进入调试模式,在watch window下name位置输入_crtBreakAlloc,在value处输入引起内存泄漏的内存块号,这里是输入67,继续运行后程序会在引发内存泄漏的位置(这里是第67块内存分配位置)中断。

watch window

这时调出函数堆栈(VS:调试->窗口->调用堆栈),查看整个程序的调用堆栈一般来说根据调用关系知道引起内存泄漏的原因。

调用堆栈

注意调试过程中根据运行库指定监视name

这里写图片描述

在Multi-threaded Debug(/MTd)即静态链接下name项输入_crtBreakAlloc;
在Multi-threaded Debug DLL(/MDd)即动态链接下name项输入{,,msvcr90d.dll}_crtBreakAlloc(msvcr90d根据具体的VS版本修改)。


参考:
https://msdn.microsoft.com/en-us/library/x98tx3cf.aspx

原创粉丝点击