Home  >  Article  >  Database  >  关于MFC下检查和消除内存泄露的技巧

关于MFC下检查和消除内存泄露的技巧

WBOY
WBOYOriginal
2016-06-07 15:35:551315browse

作者:freepublic 摘要: 本人菜鸟,测试过,但是不怎么明白,保留做以后研究 本文分析了Windows环境使用MFC调试内存泄露的技术,介绍了在Windows环境下用VC查找,定位和消除内存泄露的方法技巧。 关键词:VC;CRT 调试堆函数;试探法。 编译环境 VC6.0 技术

作者:freepublic  

摘要:

本人菜鸟,测试过,但是不怎么明白,保留做以后研究
本文分析了Windows环境使用MFC调试内存泄露的技术,介绍了在Windows环境下用VC++查找,定位和消除内存泄露的方法技巧。
关键词:VC++;CRT 调试堆函数;试探法。

编译环境
VC++6.0
技术原理
检测内存泄漏的主要工具是调试器和 CRT 调试堆函数。若要启用调试堆函数,请在程序中包括以下语句:

注意 #include 语句必须采用上文所示顺序。如果更改了顺序,所使用的函数可能无法正确工作。

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

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

在添加了上面所示语句之后,可以通过在程序中包括以下语句来转储内存泄漏信息:

<span>_CrtDumpMemoryLeaks();</span>

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

<span>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. </span>

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

<span>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. </span>

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

内存分配编号(在大括号内)。
块类型(普通、客户端或 CRT)。
十六进制形式的内存位置。
以字节为单位的块大小。
前 16 字节的内容(亦为十六进制)。
定义了 _CRTDBG_MAP_ALLOC 时,还会显示在其中分配泄漏的内存的文件。文件名后括号中的数字(本示例中为 20)是该文件内的行号。

转到源文件中分配内存的行

在"输出"窗口中双击包含文件名和行号的行。
-或-

在"输出"窗口中选择包含文件名和行号的行,然后按 F4 键。

<span>_CrtSetDbgFlag </span>

如果程序总在同一位置退出,则调用 _CrtDumpMemoryLeaks 足够方便,但如果程序可以从多个位置退出该怎么办呢?不要在每个可能的出口放置一个对 _CrtDumpMemoryLeaks 的调用,可以在程序开始包括以下调用:

<span>_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); </span>

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

说明
在VC++6.0的环境下,不再需要额外的添加

<span>#define CRTDBG_MAP_ALLOC 
#include <stdlib.h> 
#include <crtdbg.h> </crtdbg.h></stdlib.h></span>

只需要按F5,在调试状态下运行,程序退出后在"输出窗口"可以看到有无内存泄露。如果出现

<span>Detected memory leaks! 
Dumping objects -> </span>

就有内存泄露。

确定内存泄露的地方
根据内存泄露的报告,有两种消除的方法:

第一种比较简单,就是已经把内存泄露映射到源文件的,可以直接在"输出"窗口中双击包含文件名和行号的行。例如

<span>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.
C:PROGRAM FILESVISUAL STUDIOMyProjectsleaktestleaktest.cpp(20)</span>

就是源文件名称和行号。

第二种比较麻烦,就是不能映射到源文件的,只有内存分配块号。

<span>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. </span>

  这种情况我采用一种"试探法"。由于内存分配的块号不是固定不变的,而是每次运行都是变化的,所以跟踪起来很麻烦。但是我发现虽然内存分配的块号是变化的,但是变化的块号却总是那几个,也就是说多运行几次,内存分配的块号很可能会重复。因此这就是"试探法"的基础。

  1. 先在调试状态下运行几次程序,观察内存分配的块号是哪几个值;
  2. 选择出现次数最多的块号来设断点,在代码中设置内存分配断点: 添加如下一行(对于第 18 个内存分配):
    <span>_crtBreakAlloc = 18; </span>
    或者,可以使用具有同样效果的 _CrtSetBreakAlloc 函数:
    <span>_CrtSetBreakAlloc(18); </span>

     

  3. 在调试状态下运行序,在断点停下时,打开"调用堆栈"窗口,找到对应的源代码处;
  4. 退出程序,观察"输出窗口"的内存泄露报告,看实际内存分配的块号是不是和预设值相同,如果相同,就找到了;如果不同,就重复步骤3,直到相同。
  5. 最后就是根据具体情况,在适当的位置释放所分配的内存。
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:推荐数据库连接池DruidNext article:VI常用技巧总结