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

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

  最简单的方法当然是借助于专业的检测工具,比较有名如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
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
#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
#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
#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
#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
#include
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif

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

C++中内存泄漏的检测方法介绍的更多相关文章

  1. 浅谈C++中内存泄漏的检测

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

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

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

  3. 【内存泄漏】 C/C++内存泄漏及其检测工具

    对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问题.已经有许多技术被研究出来以应对这个问题,比如 Smart Pointer,Garbage Collection等.Smart Po ...

  4. C/C++内存泄漏及检测 转

    C/C++内存泄漏及检测 2011-02-20 17:51 by 吴秦, 30189 阅读, 13 评论, 收藏, 编辑 “该死系统存在内存泄漏问题”,项目中由于各方面因素,总是有人抱怨存在内存泄漏, ...

  5. Android内存泄漏的检测流程、捕捉以及分析

    https://blog.csdn.net/qq_20280683/article/details/77964208 Android内存泄漏的检测流程.捕捉以及分析 简述: 一个APP的性能,重度关乎 ...

  6. 【转】C/C++内存泄漏及检测

    “该死系统存在内存泄漏问题”,项目中由于各方面因素,总是有人抱怨存在内存泄漏,系统长时间运行之后,可用内存越来越少,甚至导致了某些服务失败.内存泄漏是最难发现的常见错误之一,因为除非用完内存或调用ma ...

  7. Android开发常见的Activity中内存泄漏及解决办法

    上一篇文章楼主提到由Context引发的内存泄漏,在这一篇文章里,我们来谈谈Android开发中常见的Activity内存泄漏及解决办法.本文将会以“为什么”“怎么解决”的方式来介绍这几种内存泄漏. ...

  8. iOS内存泄漏自动检测工具PLeakSniffer

    新款objective-C内存泄漏自动检测工具 PLeakSniffer , GitHub地址 (https://github.com/music4kid/PLeakSniffer). 背景 前些天读 ...

  9. SQL Server中解决死锁的新方法介绍

    SQL Server中解决死锁的新方法介绍 数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁,通过SQL Server 2005, 现在似乎有了一种新的解决办法. 将下面的 ...

随机推荐

  1. 将程序添加到右键菜单和图标(以记事本、UltraEdit为例)

    原文:将程序添加到右键菜单(以记事本.UltraEdit为例) 如何将程序加入右键菜单,这里分别以记事本.UltraEdit为例! 以记事本程序为例: 1. 在运行中输入regedit,打开注册表,找 ...

  2. lcc之内存分配

    现在感觉这本书有些地方没有说透,如果自己的理解能力与基础知识够强的话,理解起来还好,如果不行的话,某些地方理解起来会相当的头疼. 现在来说说内存管理.当初看这一章的时候很快就过去了,感觉很多地方很好理 ...

  3. Introducing Visual Studio’s Emulator for Android

    visual studio 2015支持Android开发了. Microsoft released Visual Studio 2015 Preview this week and with it ...

  4. Modernizr

    Modernizr 目录 概述 CSS的新增class JavaScript侦测 加载器 参考链接 概述 随着HTML5和CSS3加入越来越多的模块,检查各种浏览器是否支持这些模块,成了一大难题.Mo ...

  5. (12) MVC5 EF6 Bootstrap3

    MVC5 + EF6 + Bootstrap3 (12) 新建数据 系列教程:MVC5 + EF6 + Bootstrap3 上一节:MVC5 + EF6 + Bootstrap3 (11) 排序.搜 ...

  6. AngularJS html5Mode与ASP.NET MVC路由

    AngularJS html5Mode与ASP.NET MVC路由共存 前言 很久之前便听说AngularJS,非常酷,最近也比较火,我也在持续关注这个技术,只是没有认真投入学习.前不久公司找我们部门 ...

  7. Oracle并行查询出错

    1.错误描写叙述 ORA-12801: 并行查询服务器P007中发出错误信号 ORA-01722:无效数字 12801.00000 -"error signaled in parallel ...

  8. 使用oracle的exp命令时,提示出--hash: exp: command not found

    使用oracle的exp命令时,提示出--hash: exp: command not found 原因:当你在终端使用exp的命名时,当前的账户,并不是oracle认可的账户. 在安装oracle时 ...

  9. Java 集合之LinkedList源码分析

    1.介绍 链表是数据结构中一种很重要的数据结构,一个链表含有一个或者多个节点,每个节点处理保存自己的信息之外还需要保存上一个节点以及下一个节点的指针信息.通过链表的表头就可以访问整个链表的信息.Jav ...

  10. 使用OpenWrt的SDK

    原文:http://wiki.openwrt.org/doc/howto/obtain.firmware.sdk 为什么要使用SDK: Reasons for using the SDK are: C ...