1 MFC内存
一个内存泄漏信息指出每个内存泄漏块的类型为普通、客户端或者CRT型。在实际程序中,普通型和客户端型式最常见的类型。
普通型内存块(Normal Blocks)是你的程序平常分配的内存类型。
客户端型内存块( Client Blocks)是MFC程序给需要析构的对象分配的内存块。MFC的new操作可以选择普通型或客户端型中合适的一种作为将要被创建的对象的内存块类型。
CRT内存块(CRT Blocks)是CRT库为自己使用而分配的内存块。CRT在处理自己的释放内存操作时使用这些块,所以在内存泄漏报告中这种类型并不常见,除非发生严重异常(例如:CRT库出错)。
自由块(Free Blocks),它是已经被释放的内存块;
忽略块(Ignore Blocks.),它是已经被特殊标示的内存块。
2 检测方法
2.1 一、直接定位法
2.1.1 输出内存泄漏信息
1、在stdafx.h头文件中添加两行代码
#define _CRTDBG_MAP_ALLO
#include <crtdbg.h>
2、添加函数
_CrtDumpMemoryLeaks();//程序只有一个退出点时,在程序退出的地方添加
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); //程序有多个退出点时,在程序开始的地方添加
2.1.2 定位
如果在输出窗口直接显示了内存泄漏的位置和行数,直接双击文件显示的那行,自动跳到内存泄漏的位置。
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.
2.2 二、二分法查找内存泄漏,比较内存状态
使用_CrtMemCheckpoint捕捉内存快照,使用_CrtMemState 结构存储内存快照,在可能出现内存泄漏的程序段的开始和结尾分别捕捉内存快照,snapshot1和snapshot2,使用_CrtMemDifference比较snapshot1与snapshot2内存状态的差异,分析出内存泄漏,依次缩小程序代码范围锁定内存泄漏位置。你可以使用_CrtMemDumpStatistics输出_CrtMemState 内存结构中内容。
使用示例
_CrtMemState sOld, sNew, sDif;
_CrtMemCheckpoint(&sOld);
int *pIndex = new int[10];
_CrtMemCheckpoint(&sNew);
if(_CrtMemDifference(&sDif,&sOld,&sNew))
_CrtMemDumpStatistics(&sDif);
输出(Output)窗口
0 bytes in 0 Free Blocks.
40 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 40 bytes.
Total allocations: 40 bytes.
2.3 三、指定申请内存的序号
使用 _CrtSetBreakAlloc()或者直接给_crtBreakAlloc赋值为内存申请的序号N,启动程序后,程序在内存申请到序号N时自动中断,我们就可以【调用堆栈】窗口来判断是那块内存申请出错。
使用示例
//程序开始或该分配内存序号之前,如第18次申请的内存块
_CrtSetBreakAlloc(18);// _crtBreakAlloc = 18;
3 检测代码整理
// Debug.h: interface for the CDebug class.
//---------------------------------------------------------------------------------
// 内存泄露信息示例 :
// {49} normal block at 0x00382F78, 40 bytes long.
// Data: < > CD CDCD CD CD CD CD CD CD CD CD CD CD CD CD CD
//---------------------------------------------------------------------------------
// 显示信息包含:
// 1.内存分配的编号(大括弧中的数字);
// 2.内存快的类型(普通型、客户端型、CRT型);
// <1>普通型内存块是你的程序平常分配的内存类型。
// <2>客户端型内存块是MFC程序给需要析构的对象分配的内存块。
// <3>CRT内存块是CRT库为自己使用而分配的内存块。
// <4>自由块,它是已经被释放的内存块;
// <5>忽略块,它是已经被特殊标示的内存块。
// 3.16进制表示的内存位置;
// 4.内存快的大小;
// 5.前16bytes的内容。
//
#if !defined(AFX_DEBUG_H__6B201A16_E36F_4830_A4F5_BD2207106871__INCLUDED_)
#define AFX_DEBUG_H__6B201A16_E36F_4830_A4F5_BD2207106871__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// 一般在入口函数cpp中添加以下定义和头文件 CRT库
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
// 内存泄露信息中显示文件名和代码行号
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
class CDebug
{
private:
CDebug();
virtual ~CDebug();
public:
static void Debug()
{
// 一般在入口函数一开始添加以下代码
_CrtDumpMemoryLeaks();
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
};
// 根据内存分配编号设置断点:
static void Debug(unsigned int num)
{
//num就是刚刚检测出来的内存泄露的地方大括号内的数字,跳转到内存泄露的地方
_CrtSetBreakAlloc(num);
}
};
#endif //!defined(AFX_DEBUG_H__6B201A16_E36F_4830_A4F5_BD2207106871__INCLUDED_)