C/C++内存泄漏及检测

内存泄露的原因

1.new,malloc后没有delete,free
2.创建内核对象(比如CreateFile,CreateMutex,CreateThread),后没有释放内核对象句柄.
3.创建内存映射文件,CreateFileMapping,MapViewOfFile后没有CloseHandle(),UnMapviewofFile
4.创建GDI对象后,比如LoadIcon,LoadImage,CreateImageList等等,没有Destroy掉
5.创建DC后,比如GetDC(), 没有释放DC句柄

6保留虚拟地址空间 VirtualAlloc(),然后提交物理存储器后,没有释放掉

 

 

 

通过以下例子来介绍如何检测内存泄漏问题:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdlib.h>
#include <iostream>
using namespace std;
  
void GetMemory( char *p, int num)
{
     p = ( char *) malloc ( sizeof ( char ) * num); //使用new也能够检测出来
}
  
int main( int argc, char ** argv)
{
     char *str = NULL;
     GetMemory(str, 100);
     cout<< "Memory leak test!" <<endl;
     //如果main中存在while循环调用GetMemory
     //那么问题将变得很严重
     //while(1){GetMemory(...);}
     return 0;
}

2、Windows平台下的内存泄漏检测

2.1、检测是否存在内存泄漏问题

Windows平台下面Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法,原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏。在vs中启用内存检测的方法如下:

  • STEP1,在程序中包括以下语句: (#include 语句必须采用上文所示顺序。 如果更改了顺序,所使用的函数可能无法正常工作。)
?
1
2
3
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

通过包括 crtdbg.h,将 malloc free 函数映射到它们的调试版本,即 _malloc_dbg _free_dbg,这两个函数将跟踪内存分配和释放。 此映射只在调试版本(在其中定义了_DEBUG)中发生。 发布版本使用普通的mallocfree 函数。

#define 语句将 CRT 堆函数的基版本映射到对应的“Debug”版本。 并非绝对需要该语句;但如果没有该语句,内存泄漏转储包含的有用信息将较少。

  • STEP2, 在添加了上述语句之后,可以通过在程序中包括以下语句(通常应恰好放在程序退出位置之前)来转储内存泄漏信息:
?
1
_CrtDumpMemoryLeaks();

此时,完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
  
#include <iostream>
using namespace std;
  
void GetMemory( char *p, int num)
{
     p = ( char *) malloc ( sizeof ( char ) * num);
}
  
int main( int argc, char ** argv)
{
     char *str = NULL;
     GetMemory(str, 100);
     cout<< "Memory leak test!" <<endl;
     _CrtDumpMemoryLeaks();
     return 0;
}

当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在“输出”窗口中显示内存泄漏信息。 内存泄漏信息如下所示:

image

如果没有使用 #define _CRTDBG_MAP_ALLOC 语句,内存泄漏转储将如下所示:

image

未定义 _CRTDBG_MAP_ALLOC 时,所显示的会是:

  • 内存分配编号(在大括号内)。

  • 块类型(普通、客户端或 CRT)。

  • “普通块”是由程序分配的普通内存。

  • “客户端块”是由 MFC 程序用于需要析构函数的对象的特殊类型内存块。 MFC new 操作根据正在创建的对象的需要创建普通块或客户端块。

  • “CRT 块”是由 CRT 库为自己使用而分配的内存块。 CRT 库处理这些块的释放,因此您不大可能在内存泄漏报告中看到这些块,除非出现严重错误(例如 CRT 库损坏)。

从不会在内存泄漏信息中看到下面两种块类型:

  • “可用块”是已释放的内存块。

  • “忽略块”是您已特别标记的块,因而不出现在内存泄漏报告中。

  • 十六进制形式的内存位置。

  • 以字节为单位的块大小。

  • 前 16 字节的内容(亦为十六进制)。

定义了 _CRTDBG_MAP_ALLOC 时,还会显示在其中分配泄漏的内存的文件。 文件名后括号中的数字(本示例中为 10)是该文件中的行号。

注意:如果程序总是在同一位置退出,调用 _CrtDumpMemoryLeaks 将非常容易。 如果程序从多个位置退出,则无需在每个可能退出的位置放置对 _CrtDumpMemoryLeaks 的调用,而可以在程序开始处包含以下调用:

1
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

该语句在程序退出时自动调用 _CrtDumpMemoryLeaks。 必须同时设置_CRTDBG_ALLOC_MEM_DF_CRTDBG_LEAK_CHECK_DF 两个位域,如前面所示。

 

2.2、定位具体的内存泄漏地方

通过上面的方法,我们几乎可以定位到是哪个地方调用内存分配函数malloc和new等,如上例中的GetMemory函数中,即第10行!但是不能定位到,在哪个地方调用GetMemory()导致的内存泄漏,而且在大型项目中可能有很多处调用GetMemory。如何要定位到在哪个地方调用GetMemory导致的内存泄漏?

定位内存泄漏的另一种技术涉及在关键点对应用程序的内存状态拍快照。 CRT 库提供一种结构类型_CrtMemState,您可用它存储内存状态的快照:

?
1
_CrtMemState s1, s2, s3;

若要在给定点对内存状态拍快照,请向 _CrtMemCheckpoint 函数传递 _CrtMemState 结构。 该函数用当前内存状态的快照填充此结构:

?
1
_CrtMemCheckpoint( &s1 );

通过向 _CrtMemDumpStatistics 函数传递 _CrtMemState 结构,可以在任意点转储该结构的内容:

?
1
_CrtMemDumpStatistics( &s1 );

若要确定代码中某一部分是否发生了内存泄漏,可以在该部分之前和之后对内存状态拍快照,然后使用 _CrtMemDifference 比较这两个状态:

?
1
2
3
4
5
6
_CrtMemCheckpoint( &s1 );
// memory allocations take place here
_CrtMemCheckpoint( &s2 );
  
if ( _CrtMemDifference( &s3, &s1, &s2) )
    _CrtMemDumpStatistics( &s3 );

顾名思义,_CrtMemDifference 比较两个内存状态(s1 和 s2),生成这两个状态之间差异的结果(s3)。 在程序的开始和结尾放置_CrtMemCheckpoint 调用,并使用_CrtMemDifference 比较结果,是检查内存泄漏的另一种方法。 如果检测到泄漏,则可以使用_CrtMemCheckpoint 调用通过二进制搜索技术来划分程序和定位泄漏。

调试时,程序输出如下结果:

 

3、Linux平台下的内存泄漏检测

在上面我们介绍了,vs中在代码中“包含crtdbg.h,将 malloc free 函数映射到它们的调试版本,即 _malloc_dbg _free_dbg,这两个函数将跟踪内存分配和释放。 此映射只在调试版本(在其中定义了_DEBUG)中发生。 发布版本使用普通的mallocfree 函数。”即为malloc和free做了钩子,用于记录内存分配信息。

Linux下面也有原理相同的方法——mtrace,http://en.wikipedia.org/wiki/Mtrace

 

2、非常强大的工具valgrind

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C VLD(Visual Leak Detector)是一个针对C/C++程序的内存泄漏检测工具。内存泄漏指的是程序运行时分配的内存没有被释放,造成内存的浪费。下面我将分别介绍C VLD内存泄漏的原因及解决办法。 内存泄漏的原因主要有以下几点: 1. 未正确释放分配的内存:程序中使用了malloc、calloc等函数动态分配内存,但在使用完后未调用free进行释放,导致内存泄漏。 2. 函数返回值未释放:在函数中使用malloc等函数分配内存,但在函数返回后没有进行释放,使得此内存无法被利用。 3. 指针重复赋值:当一个指针值被重复赋值时,之前分配的内存就无法被释放,从而引起内存泄漏。 4. 被动态分配的内存忘记释放:自定义的数据结构中使用了malloc或者new分配的内存,在数据结构销毁之前未进行释放,导致内存泄漏。 解决内存泄漏的办法可以是: 1. 在每次动态分配内存后,确保及时释放:使用free或者delete运算符进行内存释放。 2. 遵循“谁分配谁释放”的原则:在哪里分配内存,在哪里释放内存。 3. 防止内存重复赋值:使用指针前,确保之前分配的内存已被释放。 4. 使用智能指针:C++提供了智能指针的机制,如shared_ptr和unique_ptr,它们可以自动管理内存,减少手动释放内存的疏忽。 在使用C VLD工具时,可以通过代码编译时在源代码中包含vld.h头文件,并将vld.lib或者vld_dlls.lib添加到代码的链接库中。然后在程序运行时,vld.dll会自动加载并对内存泄漏进行检测,输出相应的信息。通过定位泄漏的位置,可以针对性地修复代码中的问题,最终解决内存泄漏

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值