首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复。

最简单的方法当然是借助于专业的检测工具,比较有名如BoundsCheck,功能非常强大,相信做C++开发的人都离不开它。此外就是不使用任何工具,而是自己来实现对内存泄露的监控,分如下两种情况:

一. 在 MFC 中检测内存泄漏

假如是用MFC的程序的话,很简单。默认的就有内存泄露检测的功能。

我们用VS2005生成了一个MFC的对话框的程序,发现他可以自动的检测内存泄露.不用我们做任何特殊的操作. 仔细观察,发现在每个CPP文件中,都有下面的代码:
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

DEBUG_NEW 这个宏定义在afx.h文件中,就是它帮助我们定位内存泄漏。

    在含有以上代码的cpp文件中分配内存后假如没有删除,那么停止程序的时候,VisualStudio的Output窗口就会显示如下的信息了:
Detected memory leaks!
Dumping objects ->
d:\code\mfctest\mfctest.cpp(80) : {157} normal block at 0x003AF170, 4 bytes long.
 Data: < > 00 00 00 00 
Object dump complete.

在Output窗口双击粗体字那一行,那么IDE就会打开该文件,定位到该行,很容易看出是哪出现了内存泄露。

二.检测纯C++的程序内存泄露

我试了下用VisualStudio建立的Win32 Console Application和Win32 Project项目,结果都不能检测出内存泄露。

下面一步一步来把程序的内存泄露检测的机制建立起来。

首先,我们需要知道C运行库的Debug版本提供了许多检测功能,使得我们更容易的Debug程序。在MSDN中有专门的章节讲这个,叫做Debug Routines,建议大家先看看里面的内容吧。

我们会用到里面很重要的几个函数。其中最重要的是 _CrtDumpMemoryLeaks();自己看MSDN里的帮助吧。使用这个函数,需要包含头文件crtdbg.h

该函数只在Debug版本才有用,当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在“Output(输出)”窗口中显示内存泄漏信息.写段代码试验一下吧,如下:

 检测内存泄露版本一:
#include "stdafx.h"
#include <crtdbg.h>

int _tmain(int argc, _TCHAR* argv[])

{

    int* p = new int();

    _CrtDumpMemoryLeaks();

    return 0;

}

 运行后,在Output(输出)窗口,显示了如下的信息:

Detected memory leaks!

Dumping objects ->

{112} normal block at 0x003AA770, 4 bytes long.

 Data: <    > 00 00 00 00 

Object dump complete.

 但是这个只是告诉我们程序有内存泄露,到底在哪泄露了一眼看不出来啊。

   看我们的检测内存泄露版本二:

#include "stdafx.h"

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)

#else

#define DEBUG_CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include  <crtdbg.h>

#ifdef _DEBUG

#define new DEBUG_CLIENTBLOCK

#endif

int _tmain(int argc, _TCHAR* argv[])

{

    int* p = new int();

    _CrtDumpMemoryLeaks();

    return 0;

}

  该程序定义了几个宏,通过宏将Debug版本下的new给替换了,新的new记录下了调用new时的文件名和代码行.运行后,可以看到如下的结果:

Detected memory leaks!

Dumping objects ->

d:\code\consoletest\consoletest.cpp(21) : {112} client block at 0x003A38B0, subtype 0, 4 bytes long.

 Data: <    > 00 00 00 00 

Object dump complete.

 呵呵,已经和MFC程序的效果一样了,但是等一等。看下如下的代码吧:

int _tmain(int argc, _TCHAR* argv[])

{

    int* p = new int();

    _CrtDumpMemoryLeaks();

    delete p;

    return 0;

}

运行后可以发现我们删除了指针,但是它仍然报内存泄露。所以可以想象,每调用一次new,程序内部都会将该调用记录下来,类似于有个数组记录,假如delete了,那么就将其从数组中删除,而_CrtDumpMemoryLeaks()就是把这个数组当前的状态打印出来。

所以除了在必要的时候Dump出内存信息外,最重要的就是在程序退出的时候需要掉用一次_CrtDumpMemoryLeaks();

假如程序有不止一个出口,那么我们就需要在多个地方都调用该函数。

更进一步,假如程序在类的析构函数里删除指针,怎么办?例如:

#include "stdafx.h"

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)

#else

#define DEBUG_CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include  <crtdbg.h>

#ifdef _DEBUG

#define new DEBUG_CLIENTBLOCK

#endif

class Test

{

public:

    Test()      {   _p = new int();     }

    ~Test()     {   delete _p;          }

    int* _p;

};

int _tmain(int argc, _TCHAR* argv[])

{

    int* p = new int();

    delete p;

    Test t;

    _CrtDumpMemoryLeaks();

    return 0;

}

  可以看到析构函数在程序退出的时候才调用,明明没有内存泄露,但是这样的写法还是报了。

  如何改进呢,看检测内存泄露版本三:

#include "stdafx.h"

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)

#else

#define DEBUG_CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>

#ifdef _DEBUG

#define new DEBUG_CLIENTBLOCK

#endif

class Test

{

public:

    Test()      {   _p = new int();     }

    ~Test()     {   delete _p;          }

    int* _p;

};

int _tmain(int argc, _TCHAR* argv[])

{

    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

    int* p = new int();

    delete p;

    Test t;

    return 0;

}

 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );该语句在程序退出时自动调用 _CrtDumpMemoryLeaks。必须同时设置 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF.

这样,该版本已经达到了MFC一样的效果了,但是我觉得光这样还不够,因为我们只是在Output窗口中输出信息,对开发人员的提醒还不明显,经常会被遗漏,而且很多人就算发现了内存泄露,但是不好修复,不会严重影响到程序外在表现,都不会修复。怎么样能让开发人员主动的修复内存泄露的问题呢?记得曾经和人配合写程序,我的函数参数有要求,不能为空,但是别人老是传空值,没办法了,只好在函数开始验证函数参数,给他assert住,这样程序运行时老是不停的弹出assert,调试程序那个烦压,最后其他程序员烦了,就把这个问题给改好了,输入参数就正确了。所以我觉得咱要让程序员主动去做一件事,首先要让他觉得做这个事是能减轻自己负担,让自己工作轻松的。呵呵,那咱们也这样,当程序退出时,检测到内存泄露就让程序提示出来。

 看检测内存泄露版本四:

#include "stdafx.h"

#include 

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)

#else

#define DEBUG_CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include  <crtdbg.h>

#ifdef _DEBUG

#define new DEBUG_CLIENTBLOCK

#endif

void Exit()

{

    int i = _CrtDumpMemoryLeaks();

    assert( i == 0);

}

int _tmain(int argc, _TCHAR* argv[])

{

    atexit(Exit);

    int* p = new int();

    return 0;

}

该版本会在程序退出时检查内存泄露,假如存在就会弹出提示对话框.

 atexit(Exit);设置了在程序退出时执行Exit()函数。Exit()函数中,假如存在内存泄露,_CrtDumpMemoryLeaks()会返回非0值,就会被assert住了。

到这个版本已经达到可以使用的程度了。但是我们还可以做些改进,因为真要准确的检测到代码中所有的内存泄露,需要把代码中的#define……拷贝到所有使用new的文件中。不可能每个文件都拷贝这么多代码,所以我们可以将他提取出来,放在一个文件中,比如我是放在KDetectMemoryLeak.h中,该文件内容如下:

#pragma once

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)

#else

#define DEBUG_CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include <stdlib.h>

#include <crtdbg.h>

#ifdef _DEBUG

#define new DEBUG_CLIENTBLOCK

#endif

然后将KDetectMemoryLeak.h包含在项目的通用文件中,例如用VS建的项目就将其包含在stdafx.h中。或者我自己建的一个Common.h文件中,该文件包含一些通用的,基本所有文件都会用到的代码。

浅谈C++中内存泄漏的检测的更多相关文章

  1. [转载]浅谈C/C++内存泄漏及其检测工具

    http://dev.yesky.com/147/2356147_3.shtml 对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问题.已经有许多技术被研究出来以应对这个问题,比如Sm ...

  2. C++中内存泄漏的检测方法介绍

    C++中内存泄漏的检测方法介绍 首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复. 最简单的方法当然是借助于专业的检测工具,比较有名如BoundsCheck, ...

  3. [转]浅谈C/C++内存泄露及其检测工具

    转自:http://www.cnblogs.com/taoxu0903/archive/2007/10/27/939261.html 对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问 ...

  4. 浅谈如何避免内存泄漏(out of memory)

    1.在涉及使用Context时,对于生命周期比Activity长的对象应该使用Application的Context.凡是使用Context优先考虑Application的Context,当然它并不是 ...

  5. 浅谈C++ 内存泄漏及其检测工具

    浅谈C++ 内存泄漏及其检测工具 http://wenku.baidu.com/link?url=1DGkOOvd_ITZyB8IHAwfhCOx2tfO6id8UfuyQkAMHZU6sasaAXz ...

  6. 浅谈C中的malloc和free

    转自http://bbs.bccn.net/thread-82212-1-1.html非常感谢作者 浅谈C中的malloc和free 在C语言的学习中,对内存管理这部分的知识掌握尤其重要!之前对C中的 ...

  7. 浅谈Java中的equals和==(转)

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...

  8. 浅谈Linux中的信号处理机制(二)

    首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...

  9. 浅谈Java中的equals和==

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...

随机推荐

  1. 【BZOJ3007】拯救小云公主 二分+几何+对偶图

    [BZOJ3007]拯救小云公主 Description     英雄又即将踏上拯救公主的道路……     这次的拯救目标是——爱和正义的小云公主.     英雄来到boss的洞穴门口,他一下子就懵了 ...

  2. javascript JS递归遍历对象 使用for(variable in object)或者叫for/in和forEach方式

    1.递归遍历查找特定key值(ie9以下不支持forEach) 原文http://www.cnblogs.com/ae6623/p/5938560.html var obj = { first: &q ...

  3. Share Memory By Communicating

    Share Memory By Communicating - The Go Programming Language https://golang.google.cn/doc/codewalk/sh ...

  4. android studio发布公共类库到服务器maven仓库

    在上一篇中提到了怎么创建私有maven库,这篇主要结合android studio的使用,直接进入正题,看以下步骤 1.创建android项目 创建Project,然后加一个library的modul ...

  5. Javascript学习之三元运算符详解

    本文主要是通过实例为大家介绍javascript三元运算符相关内容,希望对初学者学习这部分内容有所帮助. 实例 <!DOCTYPE html> <html> <head& ...

  6. ansible操作模块相关

    1. 查看模块可用参数命令 ansible-doc -s module_name

  7. Normalize.css 与传统的 CSS Reset 有哪些区别?

    CSS Reset 是革命党,CSS Reset 里最激进那一派提倡不管你小子有用没用,通通给我脱了那身衣服,凭什么你 body 出生就穿一圈 margin,凭什么你姓 h 的比别人吃得胖,凭什么你 ...

  8. poj-3666

    http://vjudge.net/problem/POJ-3666 题目是dp 题目;   简单dp 离散一下就好. 我们先来讲一讲不离散的,简单的懂了,其他的也很容易. dp[i] 代表这个数列以 ...

  9. TCPDUMP 使用详情

    第一种是关于类型的关键字,主要包括host,net,port, 例如 host 210.27.48.2,指明 210.27.48.2是一台主机,net 202.0.0.0 指明 202.0.0.0是一 ...

  10. JNI——C调用JAVA

    步骤: 1. 创建虚拟机 2. 获得class 3. 实例化对象:获得构造方法(方法名为“<init>”),构造参数,调用方法 4. 调用方法:又分为获得方法,构造方法,调用方法 操作方法 ...