`
wangleide414
  • 浏览: 606790 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

内存泄露检测

 
阅读更多

通常的做法是自己封装一套malloc(), free(),,memset,memcpy等等,例如xmalloc(), xfree() ……。自己再定义一个数据结构,在分配内存的前后两头加signature,每次拷贝、释放的时候检查signature是否完整,不完整则为越界。例如:

    void* xmalloc(size_t len) {
        struct meminfo {
        char sig1[256];
        char* sig2;
        char ptr[0]
        } *ret;
        void* ptr;
        ret = malloc ( sizeof(*ret) + len + 256 );

        /* Fill signature with 0x55, 0xaa*/
        memset(ret->sig1, 0x55, sizeof(ret->sig1));
        memset((void*)ret + len + sizeof(*ret), 0xaa, 256);
       
        ret->sig2 = (void*)ret + len + sizeof(*ret);
        ptr = ret->ptr;
        return ptr;
    }

每次Copy,Free的时候检查sig1,sig2中的内容是否完整

xmalloc(),xfree()需要维护自己分配出去了多少内存(比如你可以用一个链表),定时检查内存分配量时候持续增长而没有释放,从而判断是否有内存泄漏。

这些都是userspace程序常用的方法。对于kernel,唯一的办法是反复想,无数次的检查你的代码


******************************************************************************************

VC++

今天调试程序,发现有内存泄漏但是没有提示具体是哪一行,搞得我很头疼。结果在网上搜索了一些资料,经自己实践后整理如下:

 

    第一种:通过"OutPut窗口"定位引发内存泄漏的代码(下面转,我写的没原文好,也懒得写)。

 

我们知道,MFC程序如果检测到存在内存泄漏,退出程序的时候会在调试窗口提醒内存泄漏。例如:
class CMyApp : public CWinApp
{
public:
   BOOL InitApplication()
   {
       int* leak = new int[10];
       return TRUE;
   }
};

产生的内存泄漏报告大体如下:
Detected memory leaks!
Dumping objects ->
c:/work/test.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

这挺好。问题是,如果我们不喜欢MFC,那么难道就没有办法?或者自己做? 

呵呵,这不需要。其实,MFC也没有自己做。内存泄漏检测的工作是VC++的C运行库做的。也就是说,只要你是VC++程序员,都可以很方便地检测内存泄漏。我们还是给个样例:
#include <crtdbg.h>

inline void EnableMemLeakCheck()
{
   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}

void main()
{
   EnableMemLeakCheck();
   int* leak = new int[10];
}

 运行(提醒:不要按Ctrl+F5,按F5),你将发现,产生的内存泄漏报告与MFC类似,但有细节不同,如下:
Detected memory leaks!
Dumping objects ->
{52} normal block at 0x003C4410, 40 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

为什么呢?看下面。

 
定位内存泄漏由于哪一句话引起的

你已经发现程序存在内存泄漏。现在的问题是,我们要找泄漏的根源。

一般我们首先确定内存泄漏是由于哪一句引起。在MFC中,这一点很容易。你双击内存泄漏报告的文字,或者在Debug窗口中按F4,IDE就帮你定位到申请该内存块的地方。对于上例,也就是这一句:

   int* leak = new int[10];

这多多少少对你分析内存泄漏有点帮助。特别地,如果这个new仅对应一条delete(或者你把delete漏写),这将很快可以确认问题的症结。 

我们前面已经看到,不使用MFC的时候,生成的内存泄漏报告与MFC不同,而且你立刻发现按F4不灵。那么难道MFC做了什么手脚? 

其实不是,我们来模拟下MFC做的事情。看下例: 
inline void EnableMemLeakCheck()
{
   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}

#ifdef _DEBUG
#define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void main()
{
   EnableMemLeakCheck();
   int* leak = new int[10];
}

再运行这个样例,你惊喜地发现,现在内存泄漏报告和MFC没有任何分别了。

 

 

    第二种方法:直接定位指定内存块错误的代码行(下面转)。

 

单确定了内存泄漏发生在哪一行,有时候并不足够。特别是同一个new对应有多处释放的情形。在实际的工程中,以下两种情况很典型: 

    创建对象的地方是一个类工厂(ClassFactory)模式。很多甚至全部类实例由同一个new创建。对于此,定位到了new出对象的所在行基本没有多大帮助。 
     
    COM对象。我们知道COM对象采用Reference Count维护生命周期。也就是说,对象new的地方只有一个,但是Release的地方很多,你要一个个排除。 

那么,有什么好办法,可以迅速定位内存泄漏?

答:有。

在内存泄漏情况复杂的时候,你可以用以下方法定位内存泄漏。这是我个人认为通用的内存泄漏追踪方法中最有效的手段。

我们再回头看看crtdbg生成的内存泄漏报告: 
Detected memory leaks!
Dumping objects ->
c:/work/test.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

除了产生该内存泄漏的内存分配语句所在的文件名、行号为,我们注意到有一个比较陌生的信息:{52}。这个整数值代表了什么意思呢?

其实,它代表了第几次内存分配操作。象这个例子,{52}代表了第52次内存分配操作发生了泄漏。你可能要说,我只new过一次,怎么会是第52次?这很容易理解,其他的内存申请操作在C的初始化过程调用的呗。:)

有没有可能,我们让程序运行到第52次内存分配操作的时候,自动停下来,进入调试状态?所幸,crtdbg确实提供了这样的函数:即 long _CrtSetBreakAlloc(long nAllocID)。我们加上它:
inline void EnableMemLeakCheck()
{
   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}

#ifdef _DEBUG
#define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void main()
{
   EnableMemLeakCheck();
   _CrtSetBreakAlloc(52);
   int* leak = new int[10];
}

你发现,程序运行到 int* leak = new int[10]; 一句时,自动停下来进入调试状态。细细体会一下,你可以发现,这种方式你获得的信息远比在程序退出时获得文件名及行号有价值得多。因为报告泄漏文件名及行号,你获得的只是静态的信息,然而_CrtSetBreakAlloc则是把整个现场恢复,你可以通过对函数调用栈分析(我发现很多人不习惯看函数调用栈,如果你属于这种情况,我强烈推荐你去补上这一课,因为它太重要了)以及其他在线调试技巧,来分析产生内存泄漏的原因。通常情况下,这种分析方法可以在5分钟内找到肇事者。

当然,_CrtSetBreakAlloc要求你的程序执行过程是可还原的(多次执行过程的内存分配顺序不会发生变化)。这个假设在多数情况下成立。不过,在多线程的情况下,这一点有时难以保证。

 

个人心得:我在用这种方法时开始没看懂,后来在MSDN中也找到了这方面相关的信息,后来才会用。我感觉在这方面网上介绍的不够详细,下面我就相对详细地解释一下(为什么用“相对详细”?本人比较懒)。首先说明一下,下面的函数不需要上面所添加的宏定义和"crtdbg.h"头文件,也不需要EnableMemLeakCheck()函数。只需在main函数一开始运行 _CrtSetBreakAlloc(long (4459))函数。其中4459是申请内存的序号(上面有说明),然后F5运行(不需要设断点),然后会出现“Find Source”这个对话框,点击“取消”。然后会出现“User breakpoint called from code at xxxx”的对话框,点击“确定”,会看到一些汇编的代码(不要怕,其实我也看不懂,算然原来学过点汇编),调出堆栈窗口(call stack),在其中的“main() line xxx + xxx bytes”上双击(或它的上一行双击,我的上一行是一个自定义函数,双击后直接定位到我new的地方,定位还是很准的,开始我怀疑,但最后检查果然是这地方没释放)会定位到错误行。

 

第三种:用Ctrl+B来设定,不过现在好像忘了。效果根第二种方法基本一样。

 

有人会问,既然第一种方法定位没问题,为什么还要介绍第二种?其实在实际应用中,某些内存泄漏它没有定位到哪一行的,只有内存块的序号(有可能我用的不太会用),这个时候就需要用第二种方法。



*************************************************************************************


简单说明了一下没有工具的情况如何运用VC库中的工具来检查代码的内存泄漏问题。

一: 内存泄漏

        内存泄漏是编程中常常见到的一个问题,内存泄漏往往会一种奇怪的方式来表现出来,基本上每个程序都表现出不同的方式。 但是一般最后的结果只有两个,一个是程序当掉,一个是系统内存不足。 还有一种就是比较介于中间的结果程序不会当,但是系统的反映时间明显降低,需要定时的Reboot才会正常。

        有 一个很简单的办法来检查一个程序是否有内存泄漏。就是是用Windows的任务管理器(Task Manager)。运行程序,然后在任务管理器里面查看 “内存使用”和”虚拟内存大小”两项,当程序请求了它所需要的内存之后,如果虚拟内存还是持续的增长的话,就说明了这个程序有内存泄漏问题。 当然如果内存泄漏的数目非常的小,用这种方法可能要过很长时间才能看的出来。

        当然最简单的办法大概就是用CompuWare的BoundChecker 之类的工具来检测了,不过这些工具的价格对于个人来讲稍微有点奢侈了。

        如果是已经发布的程序,检查是否有内存泄漏是又费时又费力。所以内存泄漏应该在Code的生成过程就要时刻进行检查。

二: 原因

       内存泄漏产生的原因一般是三种情况:

    分配完内存之后忘了回收;
    程序Code有问题,造成没有办法回收;
    某些API函数操作不正确,造成内存泄漏。

    1. 内存忘记回收,这个是不应该的事情。但是也是在代码种很常见的问题。分配内存之后,用完之后,就一定要回收。如果不回收,那就造成了内存的泄漏,造成内存泄漏的Code如果被经常调用的话,那内存泄漏的数目就会越来越多的。从而影响整个系统的运行。比如下面的代码:

for (int =0;I<100;I++)

{

    Temp = new BYTE[100];

}

就会产生 100*100Byte的内存泄漏。

    2. 在某些时候,因为代码上写的有问题,会导致某些内存想回收都收不回来,比如下面的代码:

Temp1 = new BYTE[100];

Temp2 = new BYTE[100];

Temp2 = Temp1;

这样,Temp2的内存地址就丢掉了,而且永远都找不回了,这个时候Temp2的内存空间想回收都没有办法。

    3. API函 数应用不当,在Windows提供API函数里面有一些特殊的API,比如FormatMessage。 如果你给它参数中有FORMAT_MESSAGE_ALLOCATE_BUFFER,它会在函数内部New一块内存Buffer出来。但是这个 buffer需要你调用LocalFree来释放。 如果你忘了,那就会产生内存泄漏。

三: 检查方法

        一 般的内存泄漏检查的确是很困难,但是也不是完全没有办法。如果你用VC的库来写东西的话,那么很幸运的是,你已经有了很多检查内存泄漏的工具,只是你想不 想用的问题了。Visual C++的Debug版本的C运行库(C Runtime Library)。它已经提供好些函数来帮助你诊断你的代码和跟踪内存泄漏。 而且最方便的地方是这些函数在Release版本中完全不起任何作用,这样就不会影响你的Release版本程序的运行效率。

        比如下面的例子里面,有一个明细的内存泄漏。当然如果只有这么几行代码的话,是很容易看出有内存泄漏的。但是想在成千上万行代码里面检查内存泄漏问题就不是那么容易了。

char * pstr = new char[5];

lstrcpy(pstr,"Memory leak");

        如 果我们在Debug版本的Code里面对堆(Heap)进行了操作,包括malloc, free, calloc, realloc, new 和 delete可以利用VC Debug运行时库中堆Debug函数来做堆的完整性和安全性检查。比如上面的代码,lstrcpy的操作明显破坏了pstr的堆结构。使其溢出,并破坏 了临近的数据。那我们可以在调用lstrcpy之后的代码里面加入 _CrtCheckMemory函数。_CrtCheckMemory函数发现前面的lstrcpy使得pstr的堆结构被破坏,会输出这样的报告:

emory check error at 0x00372FA5 = 0x79, should be 0xFD.

memory check error at 0x00372FA6 = 0x20, should be 0xFD.

memory check error at 0x00372FA7 = 0x6C, should be 0xFD.

memory check error at 0x00372FA8 = 0x65, should be 0xFD.

DAMAGE: after Normal block (#41) at 0x00372FA0.

Normal located at 0x00372FA0 is 5 bytes long.

       它 告诉说 pstr的长度应该时5个Bytes,但是在5Bytes后面的几个Bytes也被非法改写了。提醒你产生了越界操作。_CrtCheckMemory 的返回值只有TRUE和FALSE,那么你可以用_ASSERTE()来报告出错信息。 上面的语句可以换成 _ASSERTE(_CrtCheckMemory()); 这样Debug版本的程序在运行的时候就会弹出一个警告对话框,这样就不用在运行时候一直盯着Output窗口看了。这个时候按Retry,就可以进入源 代码调试了。看看问题到底出在哪里。

      其他类似的函数还有 _CrtDbgReport, _CrtDoForAllClientObjects, _CrtDumpMemoryLeaks,_CrtIsValidHeapPointer, _CrtIsMemoryBlock, _CrtIsValidPointer,_CrtMemCheckpoint, _CrtMemDifference, _CrtMemDumpAllObjectsSince, _CrtMemDumpStatistics, _CrtSetAllocHook, _CrtSetBreakAlloc, _CrtSetDbgFlag,_CrtSetDumpClient, _CrtSetReportFile, _CrtSetReportHook, _CrtSetReportMode

        这 些函数全部都可以用来在Debug版本中检查内存的使用情况。具体怎么使用这些函数就不在这里说明了,各位可以去查查MSDN。在这些函数中用处比较大 的,或者说使用率会比较高的函数是_CrtMemCheckpoint, 设置一个内存检查点。这个函数会取得当前内存的运行状态。 _CrtMemDifference 检查两种内存状态的异同。 _CrtMemDumpAllObjectsSince 从程序运行开始,或者从某个内存检查点开始Dump出堆中对象的信息。还有就是_CrtDumpMemoryLeaks当发生内存溢出的时候Dump出堆 中的内存信息。 _CrtDumpMemoryLeaks一般都在有怀疑是内存泄漏的代码后面调用。比如下面的例子:

#include <windows.h>

#include <crtdbg.h>

void main()

{

char * pstr;

pstr = new char[5];

_CrtDumpMemoryLeaks();

}

输出:

Detected memory leaks! à提醒你,代码有内存泄漏.

Dumping objects ->

{44} normal block at 0x00372DB8, 5 bytes long.

Data: < > CD CD CD CD CD

Object dump complete.

        如 果你双击包含行文件名的输出行,指针将会跳到源文件中内存被分配地方的行。当无法确定那些代码产生了内存泄漏的时候,我们就需要进行内存状态比较。在可疑 的代码段的前后设置内存检查点,比较内存使用是否有可疑的变化。以确定内存是否有泄漏。为此要先定义三个_CrtMemState 对象来保存要比较的内存状态。两个是用来比较,一个用了保存前面两个之间的区别。

_CrtMemState Sh1,Sh2,Sh_Diff;

char *pstr1 = new char[100];

_CrtMemCheckPoint(&Sh1); ->设置第一个内存检查点

char *pstr2 = new char[100];

_CrtMemCheckPoint(&Sh2); ->设置第二个内存检查点

_CrtMemDifference(&Sh_Diff, &Sh1, &Sh2); ->检查变化

_CrtMemDumpAllObjectsSince(&Sh_Diff); ->Dump变化

        如 果你的程序中使用了MFC类库,那么内存泄漏的检查方法就相当的简单了。因为Debug版本的MFC本身就提供一部分的内存泄漏检查。 大部分的new 和delete没有配对使用而产生的内存泄漏,MFC都会产生报告。这个主要是因为MFC重载了Debug版本的new 和delete操作符, 并且对前面提到的API函数重新进行了包装。在MFC类库中检查内存泄漏的Class就叫 CMemoryState,它重新包装了了_CrtMemState,_CrtMemCheckPoint, _CrtMemDifference, _CrtMemDumpAllObjectsSince这些函数。并对于其他的函数提供了Afx开头的函数,供MFC程序使用。比如 AfxCheckMemory, AfxDumpMemoryLeaks 这些函数的基本用法同上面提到的差不多。 CMemoryState和相关的函数的定义都在Afx.h这个头文件中。 有个简单的办法可以跟踪到这些函数的声明。在VC中找到MFC程序代码中下面的代码, 一般都在X.cpp的开头部分

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

        把 光标移到DEBUG_NEW上面 按F12,就可以进入Afx.h中定义这些Class和函数的代码部分。 VC中内存泄漏的常规检查办法主要是上面的两种。当然这两种方法只是针对于Debug版本的Heap的检查。如果Release版本中还有内存泄漏,那么 检查起来就麻烦很多了。

4 .总结:

        实际上Heap的内存泄漏问题是相当的好查的。VC的提供的检查工具也不太少,但是如果是栈出了什么问题,恐怕就麻烦很多了。栈出问题,一般不会产生内存泄漏,但是你的代码的逻辑上很有可能会有影响。这个是最最痛苦的事情。 编程,就是小心,小心再小心而已。
分享到:
评论

相关推荐

    vs2010内存泄露检查工具

    标题中的“vs2010内存泄露检查工具”指的是Visual Leak Detector(VLD),这是一个为Visual C++编译器设计的插件,它可以在运行时检测并报告C++程序中的内存泄漏情况。VLD能够集成到VS2010的环境中,使得开发者可以...

    C++内存泄露检测器

    使用前,需要先解压"leakfinder.tar",然后按照工具的文档或指南进行配置和运行,以检查项目的内存泄漏情况。 总之,理解并有效利用内存泄漏检测器是C++开发中必不可少的技能,它能帮助我们编写更加健壮、高效的...

    jni层内存泄漏检测工具

    JNI层内存泄漏检测工具是针对Android应用开发中的一个重要问题——JNI内存泄漏的解决方案。JNI,全称为Java Native Interface,允许Java代码与其他编程语言(如C++)交互,从而利用其性能优势。然而,由于Java和C/...

    windows下c++内存泄露检测工具使用方

    ### Windows 下 C++ 内存泄露检测工具使用详解 在 Windows 平台下进行 C++ 开发时,内存管理是一项非常重要的任务。由于 C++ 语言本身的特性,开发者需要手动管理内存分配与释放,这就很容易导致内存泄露的问题。...

    arm环境内存泄漏检测工具valgrind

    "arm环境内存泄漏检测工具valgrind"就是这样一个针对ARM平台的专业工具,它能帮助我们识别并解决内存泄漏问题。 Valgrind是一个开源的动态分析工具集,主要用于调试、性能评估和内存错误检测。在ARM环境下,...

    Windows内存泄漏排查工具

    为了解决这个问题,开发者通常会使用专门的内存泄漏检测工具。本文将详细介绍两个用于Windows平台的内存泄漏排查工具:LeakDiag和LDGrapher。 **LeakDiag** LeakDiag是由微软开发的一款轻量级内存泄漏检测工具,...

    Lua内存泄露检测工具LuaMonitor下载.rar

    本文将详细介绍Lua内存泄露检测工具LuaMonitor,以及如何利用它来提升项目性能。 LuaMonitor是一款专门针对Lua环境设计的内存泄露检测工具。它提供了一个直观的视图分析界面,帮助开发者了解程序在运行过程中内存的...

    内存泄露检查工具

    1. Valgrind:Valgrind 是一款开源的内存错误检测工具,包括内存泄露检测。它通过运行时虚拟机的方式,可以检测出内存泄露、无效指针访问、未初始化的内存访问等问题。Valgrind 支持多种平台,如 Linux、macOS 和...

    leakdiag 内存泄露检查工具

    在使用"leakdiag"之前,首先需要下载并安装对应的软件包,如提供的"leakdiag125内存泄露检测.msi"。安装完成后,用户可以通过命令行界面或者集成到开发环境中进行使用。具体操作步骤可以参考官方文档或通过搜索引擎...

    delphi内存泄露检查工具

    delphi5内存泄露检查工具,免费控件。在工程文件第一行代码处:memchk;

    VC内存泄露检查 VC内存泄露检查 VC内存泄露检查

    - 使用`_CrtSetDbgFlag`设置调试标志,开启内存泄露检测。 - 使用`_RPTFQUICK`宏来报告每块内存的分配和释放情况。 - 手动跟踪和记录每个`new`操作对应的`delete`操作。 6. **内存池技术** 内存池是一种内存...

    两个超棒的内存泄露检测工具

    Valgrind是一款开源的动态分析工具,广泛用于内存泄漏检测、性能分析以及错误检测。它通过在运行时为程序创建一个虚拟机,从而能够监控程序的内存操作。Valgrind提供了多个子工具,其中Memcheck是最著名的内存检测...

    内存泄露检测工具

    内存泄露检测工具 内存泄露是指计算机程序在运行过程中,无法释放已经分配的内存空间,从而导致内存泄露的现象。内存泄露检测工具是用于检测和解决内存泄露问题的软件工具。以下是常用的内存泄露检测工具: 1. ...

    freertos内存泄漏检测代码(ESP32)

    用于freertos嵌入式实时系统内存泄漏检测,可以实时查看内存状况,具体平台是ESP32

    linux下检查内存泄漏的工具+例子

    Valgrind的`memcheck`工具专门用于内存错误检测,`--leak-check=yes`则表示开启内存泄漏检查。 为了更好地理解Valgrind的用法,你可以参考提供的`demo`测试程序。这个演示程序可能会包含一些故意的内存泄漏,运行时...

    vc++内存泄漏检测工具

    5. **验证修复**:修复后,重新运行程序并检查报告,确认内存泄漏已被解决。 内存泄漏检测工具除了"leakdiag125"之外,还有其他知名的工具,如Valgrind(主要用于Linux环境)、Visual Leak Detector (VLD)、Dr. ...

    linux内存泄露检测工具

    "Linux 内存泄露检测工具" Linux 内存泄露检测是指在 Linux 环境下检测程序内存泄露的过程。内存泄露是指程序在申请内存后没有释放,导致内存的浪费和系统性能下降。检测内存泄露可以使用 Valgrind 工具,它可以...

    Swift-MLeaksFinder腾讯开源的iOS平台的自动内存泄漏检测工具

    Swift-MLeaksFinder是腾讯开源的一款专为iOS平台设计的自动内存泄漏检测工具。这款工具基于Swift编程语言,致力于帮助开发者在应用开发过程中有效地发现并解决内存泄漏问题,从而提高应用性能和稳定性。内存泄漏是...

    内存泄漏检查类

    本话题主要探讨的是如何在C++中进行内存泄漏的检查,通过重载全局的`new/delete`以及`new[]/delete[]`操作符来实现。下面我们将深入讲解这个主题。 首先,了解内存泄漏的基本概念。内存泄漏是指程序在申请内存后,...

Global site tag (gtag.js) - Google Analytics