频 道 直 达 - 新闻 - 读书 - 培训 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 技术圈 - 博客 - BBS
51CTO.COM_中国领先的IT技术网站
找资料:

Visual C++中检测和隔离内存泄漏

作者: 出处:教程网  (  ) 砖  (  ) 好  评论 ( ) 条  进入论坛
更新时间:2005-11-10 14:39
关 键 词:内存
阅读提示:具有动态的分配和释放内存的能力是C/C++程序语言的重要特色之一。VisualC++ debugger和CRT库提供了一系列有效的检测和鉴定内存泄漏的工具。

具有动态的分配和释放内存的能力是C/C++程序语言的重要特色之一。VisualC++ debugger和CRT库提供了一系列有效的检测和鉴定内存泄漏的工具。
设置内存泄漏检测
检测内存泄漏的基本工具是调试器和CRT调试堆函数。为了使用调试堆函数,在你的程序中你必须含有下面的说明:

#define _CRTDBG_MAP_ALLOC
#include
#include

必须保证上面声明的顺序,如果改变了顺序,可能不能正常工作。的_malloc_dbg和_free_dbg将取代标准的malloc和free函数出现在DEBUG版中,它可以跟踪内存的分配和释放。但是这只会在DEBUG版本中发生(当#define _DEBUG的时候),而Release版本仍使用标准的malloc和free功能。
#define _CRTDBG_MAP_ALLOC表示使用CRT堆函数的相应的DEBUG版本。这个定义不是必须的,但是没有它,内存泄漏报告含有的只是没有什么用处的信息。
一旦你已经添加了刚才的声明,你就能够通过在程序中加入下面的代码来报告内存泄漏信息:
_CrtDumpMemoryLeaks();

当在DEBUG模式下运行程序时,在Output窗口的Debug标签处_CrtDumpMemoryLeaks会显示内存泄漏的信息,例如:
Detected memory leaks!
Dumping objects ->
C:\PROGRAM FILES\VISUAL STUDIO\MyProjects\leaktest\leaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.
Data:< > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

如果没有#define _CRTDBG_MAP_ALLOC,内存泄漏报告就会像下面这样:
Detected memory leaks!
Dumping objects ->
{18} normal block at 0x00780E80, 64 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.


由此可见,定义_CRTDBG_MAP_ALLOC时,_CrtDumpMemoryLeaks可以提供更多的有用信息。
如果没有定义_CRTDBG_MAP_ALLOC,那么内存泄漏报告如下显示:
 内存分配数值(花括号内)
 模块的类型(normal、client或者CRT)
 以十六进制格式定位的内存
 以字节计模块的大小
 第一个十六字节的内容(也可以用十六进制)
如果定义了_CRTDBG_MAP_ALLOC,报告的内容还包括出现分配所泄漏内存的文件。在文件名之后括号内的数字是文件内的行数值。
此时双击包含行数值和文件名的输出行, 或者选择输出行并按F4:
C:\PROGRAM FILES\VISUAL STUDIO\MyProjects\leaktest\leaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.
编辑窗口将会跳到文件中分配所泄漏内存的那一行代码,leaktest.cpp中的行号为20的那一行。
使用_CrtSetDbgFlag
如果你的程序只在一个地方退出,那么在选择调用_CrtDumpMemoryLeaks的位置是非常容易的。但是,如果你的程序可能会在程序多处位置退出该怎么办?如果不希望在每一个可能的出口处调用_CrtDumpMemoryLeaks,那么你可以在你的程序开始处包含下面的调用:
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
当程序退出时,将会自动地调用_CrtDumpMemoryLeaks(必须设置_CRTDBG_ALLOC_MEM_DF和 _CRTDBG_LEAK_CHECK_DF)。
翻译内存模块的类型

内存泄漏报告中把每一块泄漏的内存分为普通块、客户块和CRT块。事实上,你只需要留心普通块和客户块类型。
普通块(normal block):是由你的程序分配的内存。
客户块(client block):是一种特殊的内存块,它是由MFC使用的一个对象,程序退出时,该对象的析构函数没有被调用。MFC new操作符可以用来创建普通块和客户块。
CRT块(CRT block):是由C RunTime Library供自己使用而分配的内存块。CRT库自己来管理这些内存的分配与释放,通常你不会在内存泄漏报告中发现有CRT内存泄漏,除非程序发生了严重的错误(例如CRT库崩溃)。
下面这两种类型的内存块不会出现在内存泄漏报告中:
 空闲块(free block):已经被释放(free)的内存块。
 忽略块(Ignore block):是程序员显式声明过不要在内存泄漏报告中出现的内存块

设置CRT报告样式
通常_CrtDumpMemoryLeaks()会dump内存泄漏的信息到output窗口的Debug栏位。你可以使用_CrtSetReportMode()来重新设置输出到另一个位置。关于更详细的如何使用_CrtSetReportMode()说明,请查看MSDN。
在内存分配数目处设置一个断点
在内存泄漏报告中的文件名和行号可告诉分配泄漏的内存的代码位置,但是光是有这些信息对于完整了解泄漏原因,有时候还是不够的。因为一个程序在运行时,一段分配分配内存的代码将会被调用很多次,但可能是某次调用后没有释放内存而导致了泄漏内存。为了确定是那些内存没有被释放,你必须不仅要知道泄漏的内存在那里被分配,还要知道泄漏产生的条件。对你来说,有帮助的信息就是内存分配号——在文件名和行号之后的花括号对中出现的数值。
例如,在下面的输出信息中,"18"是内存分配号,意思是泄漏的内存是你程序中分配的第十八个内存块:

Detected memory leaks!
Dumping objects ->
C:\PROGRAM FILES\VISUAL STUDIO\MyProjects\leaktest\leaktest.cpp(20) : {18} normal block at
0x00780E80, 64 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

CRT库为在程序运行期间分配的所有内存模块计数,包括CRT自己分配的内存或者诸如MFC等分配的其它模块。因此带有分配号n的一个对象是在你的程序中分配的第n个对象,但不代表是由那段代码分配的第n个对象(在大部分情况下,它都不会是。)
这样的话,你可以利用分配号在内存分配的地方设置一个断点。为了设置这样一个端点,你可以在你的程序开始处,设置一个位置断点。当你的程序在那一点break时,你就能够从QuickWatch对话框或者Watch窗口设置这样一个位置断点。
例如,在Watch窗口中,在Name栏键入下面的表达式:
_crtBreakAlloc

如果你正在用CRT库dynamic-link library (DLL)的多线程版本,你必须含有上下文操作符,像这样:
{,,msvcrtd.dll}_crtBreakAlloc

现在按下RETURN键,调试器找到该值并把结果放置在value栏。如果你在内存分配过程中还没有设置任何内存分配断点,那么这个值是-1。用你想要去中断的内存分配的分配数值来,取代value表中的值——例如,18。
当设置完内存分配断点之后,继续调试。这时,运行程序时一定要小心,要保证内存块分配的顺序不会改变。当你的程序在内存分配点中断的时候,你就能够查看Call Stack窗口和其他的DEBUG信息来分析泄漏原因了。你仍然可以继续从那一点执行程序,以至于了解到底发生了什么,同时确定为什么内存没有被释放(设置一个内存分配断点是很有帮助的)。
虽然在调试器中设置内存分配断点通常更容易,但是如果你喜欢的话,你也可以在你的代码中设置它们。为了在你的代码中设置一个内存分配断点,可以增加这样一行(对于第十八个内存分配):
_crtBreakAlloc = 18;

你还有可以使用有相同效果的_CrtSetBreakAlloc函数:
_CrtSetBreakAlloc(18);

比较内存状态
定位内存泄漏的另一个方法就是在关键点对应用程序的内存状态做快照。CRT库提供了一个结构类型_CrtMemState。你可以用它来存储内存状态的一个快照:
_CrtMemState s1, s2, s3;

为了在特定点对内存状态进行快照,可以传递一个_CrtMemState结构到he _CrtMemCheckpoint函数。此函数用当时内存状态的一个快照来填充此结构:
_CrtMemCheckpoint( &s1 );

你可以通过传递此结构到_CrtMemDumpStatistics函数来dump _CrtMemState结构的任意点的内容:
_CrtMemDumpStatistics( &s1 );

此函数打印出类似于下面这样的一堆内存分配信息:
0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
3071 bytes in 16 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 3071 bytes.
Total allocations: 3764 bytes.

为了确定一个内存泄漏是否在一节代码中出现,你可以在此节前和此节后对内存状态作快照,然后用_CrtMemDifference比较两种状态:
_CrtMemCheckpoint( &s1 );
// memory allocations take place here
……
_CrtMemCheckpoint( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2) )
_CrtMemDumpStatistics( &s3 );

从名字上可以知道,_CrtMemDifference用来比较两个内存状态(最后面的那两个参数),并返回状态差异的结果(第一个参数)。在你的函数开始和结尾处的_CrtMemCheckpoint调用和使有_CrtMemDifference来比较结果为检测内存泄漏提供了另一种方法。如果检测到一个泄漏,那么可以使用_CrtMemCheckpoint调用来分割你的程序,并使用binary search technique来定位泄漏。


发表
查看
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 领 先 的 IT 技 术 网 站 ·
技 术 成 就 梦 想
·Java基础教程 (查看51847次)
·UML类图详解 (查看46438次)
·Java编程开发手册 (查看24908次)
·UML统一建模语言 (查看23911次)
·C#技术开发指南 (查看21990次)
·Java编程开发手册 (1195个砖)
·Java基础教程 (429个砖)
·C#技术开发指南 (304个砖)
·PB开发教程 (220个砖)
·.NET开发手册 (217个砖)
·Java编程开发手册 (653个好)
·Java基础教程 (569个好)
·.NET开发手册 (249个好)
·PB开发教程 (208个好)
·Delphi开发技术手册 (174个好)
订阅技术快讯
电子杂志下载
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
名称:Vista精品应用黄皮书
简介:《Vista精品应用黄皮书》囊括了Vista的各方面内容。此次的精简版,是将里面的内容做了提取,便于用户下载和使用。内容包含了各种Vista的安装与实施、技巧与解析以及各种Vista相关学习文档和相关软件的安全下载。该电子书是了解和应用Vista人员必备的工具手册,并且也是第一本
名称:2006中国IT论坛精品集合
简介:本书由“51CTO论坛推广联盟”制作完成。书中所有内容均来自各联盟成员的论坛(网站)。制作本书的目的是为了集中大家的优势资源,将更多更精彩的内容带给广大技术爱好者。本书是联盟成立以来制作的第一本书。
关键字阅读
频道精选
主编信箱 热线:010-66476606 告诉我们您想看的:专题 文章
关于我们 | 诚聘英才 | 联系我们 | 网站大事 | 意见反馈 | 网站地图
Copyright©2005-2007 51CTO.COM 版权所有