内存泄露,即在程序中动态分配的堆内存,没有得到释放,导致可用的堆内存越来越少,造成程序性能下降,严重者导致程序崩溃。C/C++不像Java那样有内存自动回收自制,C/C++的设计哲学就是程序员要对自己所担当的一切负责任,自由的内存分配带来了编程的灵活性,让程序员直接操作内存,程序效率更高。即使看似无害的内存泄露也要及时修正,因为对于一些长时间运行的系统或者一些长时间的后台程序,极微小的内存泄露也可能造成系统的奔溃。编程是一个很严谨的事情。
Visual Studio和c运行库(CRT)提供了检测内存泄露的方法。
启用CRT中的内存泄露检测机制
首先,来看一个程序:
<span style="font-family:SimSun;font-size:14px;">#include "stdafx.h"
</span><pre name="code" class="html"><span style="font-family:SimSun;font-size:14px;">#define _CRTDBG_MAP_ALLOC</span>
#define _DEBUG#include "stdlib.h"#include "crtdbg.h"#ifdef _DEBUG#ifndef DBG_NEW#define DBG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)#define new DBG_NEW#endif#endifvoid fun(){int *p = (int *)malloc(sizeof(int) * 10);}void main(){_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
<span style="font-family:SimSun;font-size:14px;"><span style="white-space:pre"> </span>fun();
//int *nPtr = new int[10];
}</span>
<span style="font-family:SimSun;font-size:14px;">Detected memory leaks!
Dumping objects ->
c:\users\wyp\documents\visual studio 2008\projects\2014101502\2014101502\2014101502.cpp(33) : {63} normal block at 0x01263CC8, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.</span>
为了启用CRT的内存泄露检测机制,需要以下操作:
<span style="font-family:SimSun;font-size:14px;">#define _CRTDBG_MAP_ALLOC
#include "crtdbg.h"</span>
crtdbg.h:将malloc和free或new和delete映射到它们的调试版本,它们的调试版本将跟踪内存的分配和释放。此映射只在包含_DEBUG的调试版本中有用,Release版本使用效率高一些的malloc和free或new和delete
_CRTDBG_MAP_ALLOC:将CRT堆函数的基础版本映射到对应的调试版本。省略_CRTDBG_MAP_ALLOC宏,内存泄露转储将有所简化。
内存泄露检测机制启用后,就可以使用_CrtDumpMemoryLeaks()函数在程序退出时显示内存泄露报告,该函数设置在应用程序的退出点之前,如果应用程序有多个退出点,不需要再每个退出点都手动设置一个对_CrtDumpMemoryLeaks()的调用。在应用程序开头部分使用_CrtSetDbgFlag函数设置flag就会导致在程序的每个退出点自动调用_CrtDumpMemoryLeaks函数。同时,必须设置两个标志:
<span style="font-family:SimSun;font-size:14px;">_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);</span>
内存泄露报告详解
如果应用程序未定义_CRTDBG_MAP_ALLOC,则_CrtDumpMemoryLeaks显示的内存泄露报告如下:
<span style="font-family:SimSun;font-size:14px;">Detected memory leaks!
Dumping objects ->
{63} normal block at 0x01163CC8, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.</span>
如果应用程序定义了_CRTDBG_MAP_ALLOC,则_CrtDumpMemoryLeaks显示的内存泄露报告如下:
<span style="font-family:SimSun;font-size:14px;">Detected memory leaks!
Dumping objects ->
c:\users\wyp\documents\visual studio 2008\projects\2014101502\2014101502\2014101502.cpp(33) : {63} normal block at 0x01263CC8, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.</span>
区别在于,第二份报告显示文件名,以及泄漏的内存初次分配所在位置的行号。
不论是否定义 _CRTDBG_MAP_ALLOC,内存泄漏报告都显示以下信息:
-
内存分配编号,在本例中为 18。
-
块类型,在本例中为 normal。
-
十六进制内存位置,在本例中为 0x00780E80。
-
块的大小,在本例中为 64 bytes。
-
块中前 16 个字节的数据(十六进制形式)。
内存泄漏报告将内存块标识为普通、客户端或 CRT。 “普通块”是由程序分配的普通内存。 “客户端块”是由 MFC 程序用于需要析构函数的对象的特殊类型内存块。MFC new 运算符根据正在创建的对象的需要创建普通块或客户端块。 “CRT 块”是由 CRT 库为自己使用而分配的内存块。 CRT 库可处理这些块的释放。 因此,您不大可能在内存泄漏报告中看到这些块,除非出现严重错误(例如 CRT 库损坏)。
内存泄漏报告中绝对不会出现另外两个内存块类型。 “可用块”是已释放的内存。 也就是说,根据定义,这种块不会泄漏。 “忽略块”是已明确标记、不出现在内存泄漏报告中的块。
这些方法适用于使用标准 CRT malloc 函数分配的内存。 不过,如果程序使用 C++ new 运算符分配内存,则需要重新定义 new 才能在内存泄漏报告中看到文件和行号。 您可以利用如下所示的代码块实现:
#ifdef _DEBUG
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif // _DEBUG
参考资料:
http://blog.csdn.net/genganpeng/article/details/7558156
http://blog.csdn.net/normallife/article/details/3278659