在Visual C++里面检查测试和隔离内存条泄漏

2016-02-19 21:06 21 1 收藏

最近很多朋友喜欢上设计,但是大家却不知道如何去做,别担心有图老师给你解答,史上最全最棒的详细解说让你一看就懂。

【 tulaoshi.com - 编程语言 】

具有动态的分配和释放内存的能力是C/C++程序语言的重要特色之一。VisualC++ debugger和CRT库提供了一系列有效的检测和鉴定内存泄漏的工具。

设置内存泄漏检测

检测内存泄漏的基本工具是调试器和CRT调试堆函数。为了使用调试堆函数,在你的程序中你必须含有下面的说明:

#define _CRTDBG_MAP_ALLOC

#includestdlib.h

#includecrtdbg.h

必须保证上面声明的顺序,如果改变了顺序,可能不能正常工作。crtdbg.h的_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 FILESVISUAL STUDIOMyProjectsleaktestleaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

Data: CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

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 FILESVISUAL STUDIOMyProjectsleaktestleaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

编辑窗口将会跳到文件中分配所泄漏内存的那一行代码,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 FILESVISUAL STUDIOMyProjectsleaktestleaktest.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来定位泄漏。

来源:http://www.tulaoshi.com/n/20160219/1625277.html

延伸阅读
假如你正在开发一个具有多媒体功能的通讯录程序。这个通讯录除了能存储通常的文字信息如姓名、地址、电话号码外,还能存储照片和声音(可以给出他们名字的正确发音)。 为了实现这个通信录,你可以这样设计: class Image { // 用于图像数据 public: Image(const string& imageDataFileName); ... };...
1、考虑内存的品牌 目前市场上知名的笔记本电脑内存品牌主要有三星、HY(现代)、KingMax. KingHorse、Kingston 及宇瞻(Apacer)等。这些大厂的产品在出厂时都经过严格检测,兼容性一般比杂牌产品好,稳定 性也更佳。另外,考虑品牌时,注意查看笔记本电脑中安装的内存品牌,尽量选购同品牌的产品。 2、考虑内存的种类 ...
笔记本内存如何拆卸安装及增加内存条?   笔记本电脑也会出现开机黑屏,蓝屏,显示不了,嘀嘀响,堆的英文等等。很多时候是硬件接触问题,比如:电池,内存,硬盘等等。如果笔记本电脑是内存问题,那应该怎么重新拔插安装呢?现在简单介绍一下:笔记本内存如装卸/笔记本如何增加内存条? 工具/原料 笔记本 十字螺丝刀 ...
1.对应的new和delete要采用相同的形式 下面的语句有什么错? string *stringarray = new string[100]; ... delete stringarray; 一切好象都井然有序——一个new对应着一个delete——然而却隐藏着很大的错误:程序的运行情况将是不可猜测的。至少,stringarray指向的100个string对象中的99个不会被正确地摧毁,因为他...
在进行memcpy操作时,虽然是内存操作,但是仍然是耗一点点CPU的,今天测试了一下单线程中执行memcpy的效率,这个结果对于配置TCP epoll中的work thread 数量有指导意义。如下基于8K的内存快执行memcpy, 1个线程大约1S能够拷贝500M,如果服务器带宽或网卡到上限是1G,那么网络io的work thread 开2个即可,考虑到消息的解析损耗,3个线程足以抗...

经验教程

410

收藏

53
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部