VC++ 6.0 中如何使用 CRT 调试功能来检测内存泄漏
作者:JerryZ
下载例子源代码
最近看了周星星 Blog 中的一篇文章:―VC++6.0中内存泄漏检测‖,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点一、二,然而未果。没有办法,最后我一头栽进 MSDN 库狂搜了一把,功夫不负有心人,我搜出很多有关这方面的资料,没过多久我便基本上就找到了答案...... 首先,检测内存泄漏的基本工具是调试器和 CRT 调试堆函数。为了使用调试堆函数,必须在要检测内存泄漏和调试的程序中添加下面的语句:
#define _CRTDBG_MAP_ALLOC #include
#include \
MSDN 如是说:―必须保证上面声明的顺序,如果改变了顺序,可能不能正常工作。‖至于这是为什么,我们不得而知。MS 的老大们经常这样故弄玄虚。 针对非 MFC 程序,再加上周星星的头文件:debug_new.h,当然如果不加这一句,也能检测出内存泄漏,但是你无法确定在哪个源程序文件中发生泄漏。Output 输出只告诉你在 crtsdb.h 中的某个地方有内存泄漏。我测试时 REG_DEBUG_NEW 没有起作用。加不加这个宏都可以检测出发生内存分配泄漏的文件。
其次,一旦添加了上面的声明,你就可以通过在程序中加入下面的代码来报告内存泄漏信息了:
_CrtDumpMemoryLeaks();
这就这么简单。我在周星星的例子代码中加入这些机关后,在 VC++ 调试会话(按 F5 调试运行) Output 窗口的 Debug 页便看到了预期的内存泄漏 dump。该 dump 形式如下:
Detected memory leaks! Dumping objects ->
c:\\Program Files\\...\\include\\crtdbg.h(552) : {45} normal block at 0x00441BA0, 2 bytes long. Data: 41 42
c:\\Program Files\\...\\include\\crtdbg.h(552) : {44} normal block at 0x00441BD0, 33 bytes long.
Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD c:\\Program Files\\...\\include\\crtdbg.h(552) : {43} normal block at 0x00441C20, 40 bytes long.
Data: < C > E8 01 43 00 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete.
更具体的细节请参考本文附带的源代码文件。
下面是我看过 MSDN 资料后,针对―如何使用 CRT 调试功能来检测内存泄漏?‖的问题进行了一番编译和整理,希望对大家有用。如果你的英文很棒,那就不用往下看了,建议直接去读 MSDN 库中的技术原文。
C/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:―最大的长处也可能成为最大的弱点‖,那么 C/C++ 应用程序正好印证了这句话。在 C/C++ 应用程序开发过程中,动态分配的内存处理不当是最常见的问题。其中,最难捉摸也最难检测的错误之一就是内存泄漏,即未能正确释放以前分配的内存的错误。偶尔发生的少量内存泄漏可能不会引起我们的注意,但泄漏大量内存的程序或泄漏日益增多的程序可能会表现出各种 各样的征兆:从性能不良(并且逐渐降低)到内存完全耗尽。更糟的是,泄漏的程序可能会用掉太多内存,导致另外一个程序垮掉,而使用户无从查找问题的真正根源。此外,即使无害的内存泄漏也可能殃及池鱼。
幸运的是,Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法。下面请和我一起分享收获——如何使用 CRT 调试功能来检测内存泄漏?
1. 如何启用内存泄漏检测机制?
o 使用 _CrtSetDbgFlag o 设置 CRT 报告模式
2. 解释内存块类型
3. 如何在内存分配序号处设置断点? 4. 如何比较内存状态? 5. 结论
如何启用内存泄漏检测机制?
VC++ IDE 的默认状态是没有启用内存泄漏检测机制的,也就是说即使某段代码有内存泄漏,调试会话的 Output 窗口的 Debug 页不会输出有关内存泄漏信息。你必须设定两个最基本的机关来启用内存泄漏检测机制。
一是使用调试堆函数:
#define _CRTDBG_MAP_ALLOC #include
注意:#include 语句的顺序。如果更改此顺序,所使用的函数可能无法正确工作。
通过包含 crtdbg.h 头文件,可以将 malloc 和 free 函数映射到其―调试‖版本 _malloc_dbg 和 _free_dbg,这些函数会跟踪内存分配和释放。此映射只在调试(Debug)版本(也就是要定义 _DEBUG)中有效。发行版本(Release)使用普通的 malloc 和 free 函数。
#define 语句将 CRT 堆函数的基础版本映射到对应的―调试‖版本。该语句不是必须的,但如果没有该语句,那么有关内存泄漏的信息会不全。
二是在需要检测内存泄漏的地方添加下面这条语句来输出内存泄漏信息:
_CrtDumpMemoryLeaks();
当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在 Output 窗口的 Debug 页中显示内存泄漏信息。比如:
Detected memory leaks! Dumping objects ->
C:\\Temp\\memleak\\memleak.cpp(15) : {45} normal block at 0x00441BA0, 2 bytes long. Data: 41 42
c:\\program files\\microsoft visual
studio\\vc98\\include\\crtdbg.h(552) : {44} normal block at 0x00441BD0, 33 bytes long.
Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD c:\\program files\\microsoft visual
studio\\vc98\\include\\crtdbg.h(552) : {43} normal block at 0x00441C20, 40 bytes long.
Data: < C > 08 02 43 00 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete.
如果不使用 #define _CRTDBG_MAP_ALLOC 语句,内存泄漏的输出是这样的:
Detected memory leaks!
Dumping objects ->
{45} normal block at 0x00441BA0, 2 bytes long. Data: 41 42
{44} normal block at 0x00441BD0, 33 bytes long.
Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD {43} normal block at 0x00441C20, 40 bytes long.
Data: < C > C0 01 43 00 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete.
根据这段输出信息,你无法知道在哪个源程序文件里发生了内存泄漏。下面我们来研究一下输出信息的格式。第一行和第二行没有什么可说的,从第三行开始:
xx}:花括弧内的数字是内存分配序号,本文例子中是 {45},{44},{43}; block:内存块的类型,常用的有三种:normal(普通)、client(客户端)或 CRT(运行时);本文例子中是:normal block; 用十六进制格式表示的内存位置,如:at 0x00441BA0 等; 以字节为单位表示的内存块的大小,如:32 bytes long; 前 16 字节的内容(也是用十六进制格式表示),如:Data: 41 42 等;
仔细观察不难发现,如果定义了 _CRTDBG_MAP_ALLOC ,那么在内存分配序号前面还会显示在其中分配泄漏内存的文件名,以及文件名后括号中的数字表示发生泄漏的代码行号,比如:
C:\\Temp\\memleak\\memleak.cpp(15)
双击 Output 窗口中此文件名所在的输出行,便可跳到源程序文件分配该内存的代码行(也可以选中该行,然后按 F4,效果一样) ,这样一来我们就很容易定位内存泄漏是在哪里发生的了,因此,_CRTDBG_MAP_ALLOC 的作用显而易见。
使用 _CrtSetDbgFlag
如果程序只有一个出口,那么调用 _CrtDumpMemoryLeaks 的位置是很容易选择的。但是,如果程序可能会在多个地方退出该怎么办呢?在每一个可能的出口处调用 _CrtDumpMemoryLeaks 肯定是不可取的,那么这时可以在程序开始处包含下面的调用:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
这条语句无论程序在什么地方退出都会自动调用 _CrtDumpMemoryLeaks。注意:这里必须同时设置两个位域标志:_CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF。
设置 CRT 报告模式
默认情况下,_CrtDumpMemoryLeaks 将内存泄漏信息 dump 到 Output 窗口的 Debug 页, 如果你想将这个输出定向到别的地方,可以使用 _CrtSetReportMode 进行重置。如果你使用某个库,它可能将输出定向到另一位置。此时,只要使用以下语句将输出位置设回 Output 窗口即可:
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );
有关使用 _CrtSetReportMode 的详细信息,请参考 MSDN 库关于 _CrtSetReportMode 的描述。
解释内存块类型
前面已经说过,内存泄漏报告中把每一块泄漏的内存分为 normal(普通块)、client(客户端块)和 CRT 块。事实上,需要留心和注意的也就是 normal 和 client,即普通块和客户端块。
? normal block(普通块):这是由你的程序分配的内存。
? client block(客户块):这是一种特殊类型的内存块,专门用于 MFC 程
序中需要析构函数的对象。MFC new 操作符视具体情况既可以为所创建的对象建立普通块,也可以为之建立客户块。
? CRT block(CRT 块):是由 C RunTime Library 供自己使用而分配的内
存块。由 CRT 库自己来管理这些内存的分配与释放,我们一般不会在内存泄漏报告中发现 CRT 内存泄漏,除非程序发生了严重的错误(例如 CRT 库崩溃)。
除了上述的类型外,还有下面这两种类型的内存块,它们不会出现在内存泄漏报告中:
? free block(空闲块):已经被释放(free)的内存块。
? Ignore block(忽略块):这是程序员显式声明过不要在内存泄漏报告中
出现的内存块。
如何在内存分配序号处设置断点?
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库VC6中内存泄漏检测在线全文阅读。
相关推荐: