分类
使用VC++和CRT库定位和排除内存泄漏‖
| 动态分配、回收内存是 我不知道是谁?那位知道? 正确,在内存处理出错的地 泄漏-没有把前边分配的内 大块内存,或者渐增式的泄 耗尽错误。最坏的是,一个 ,留给用户的是不能知道错 问题的先兆。幸运的是VC++ 文描述如何使用这些工具有 | C/C++编程语言一个最强的特点 ) 指出,最强的同时也是最弱 方通常就是BUGS产生的地方。一 存成功释放,一个小的内存泄漏 漏内存可能引起的现象是:先是 内存泄漏程序可能用完了如此多 误到底来自哪里。另外,一个看 DEBUGER和CRT库提供了一组有效 效和系统的排除内存泄漏。 | ,但是中国哲学家孙(Sun Tzu, 的。饩浠岸訡/C++应用来说非常 个最敏感和难检测的BUG就是内存 可能不需要太注意,但是程序泄漏 性能低下,再就是引起复杂的内存 的内存以至于引起其他的程序出错 上去无害的内存泄漏可能是另一个 的检测和定位内存泄漏的工具。本 |
| 启动内存泄漏检测: |
| 主要的检测工具是DEBU 含以下几个语句: | GER和CRT堆除错函数。要使除错 | 函数生效,必须要在你的程序中包 |
| #define _CRTDBG_MAP_ALLOC |
| #include "stdlib.h" |
| #include "crtdbg.h" |
| 并且这些#include 语 的函数工作不正常。包含cr 和 _free_dbg)来替换他们 效,Relese版本中还是使用 | 句必须按上边给出的顺序使用。 tdbg.h的作用是用malloc和free ,他们能跟倌诖娣峙浜突厥铡?br>普通的malloc和free函数。 | 如果你改变了顺序,可能导致使用 函数的debug版本(_malloc_dbg 这个替换仅仅是在debug状态下生 |
| 上面的#define语句使用crt堆函数相 必需的,但是没有他,你可能会失去一些 | 应的debug版本来替换正常的堆函数。这个语句不是 有用的内存泄漏信息。 |
| 你一旦在你的程序中增加了以上的语 _CrtDumpMemoryLeaks();函数来输出内存 | 句,你可以通过在程序中增加 泄漏信息。 |
| 当你在debuger下运行 口的Debug标签项里。内存 | 你的程序时,_CrtDumpMemoryLe 泄漏信息举例如下: | aks 显示内存泄漏信息在OutPut窗 |
| Detected memory leaks! |
| Dumping objects -> |
| C:PROGRAM FILESVISUAL STUDIOM | yProjectsleaktestleaktest.cpp(20) : {18} |
| normal block at 0x00 | 780E80, 64 bytes long. |
| Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD |
| Object dump complete. |
| 如果你没有使用 #defi | ne _CRTDBG_MAP_ALLOC语句的话 | ,输出信息将如下: |
| 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. |
| 像你所看到的,当_CRTDBG_MAP_ALLO 信息。在没有定义_CRTDBG_MAP_ALLOC 的 | C 被定义后_CrtDumpMemoryLeaks给了你很多有用的 情况下,显示信息包含: |
| 1.内存分配的编号(大括弧中的数字); |
| 2.内存快的类型(普通型、客户端型、CRT型); |
| 3.16进制表示的内存位置; |
| 4.内存快的大小; |
| 5.前16bytes的内容。 |
| 如果定义了_CRTDBG_MAP_ALLOC ,输 定位信息。文件名后圆括弧中的数字是行 | 出信息还包含当前泄漏内存是在那个文件中被分配的 数。如果你双击这行信息, |
| C:PROGRAM FILESVIS | UAL STUDIOMyProjectsleakte | stleaktest.cpp(20) : {18} |
| normal block at 0x00 | 780E80, 64 bytes long. |
| 光标就会跳转到原文件中分配这个内 的效果。 | 存的行前。选择Output中的题是行,按F4能达到同样 |
| 使用Using _CrtSetDbgFlag: |
| 如果你的程序的退出点 如果你的程序有多个退出点 _CrtDumpMemoryLeaks,你 | 只有一个的话,调用_CrtDumpMe 话会是什么样一个情况?如果不 可以在程序的开始包含以下调用 | moryLeaks将是非常容易。但是, 想在每个退出点都调用 : |
| _CrtSetDbgFlag( _CRT | DBG_ALLOC_MEM_DF | _CRTDBG_L | EAK_CHECK_DF); |
| 这个语句会在你的程序结束时自动调 那样设置_CRTDBG_ALLOC_MEM_DF 和 _CRT | 用_CrtDumpMemoryLeaks,但是你必须象前边提到的 DBG_LEAK_CHECK_DF这两个标志位。 |
| 介绍一下内存块的类型: |
| 就象前面指出的,一个 型。在实际程序中,普通型 | 内存泄漏信息指出每个内存泄漏 和客户端型式最常见的类型。 | 块的类型为普通、客户端或者CRT |
| 普通型内存块是你的程序平常分配的内存类型。 |
| 客户端型内存块是MFC程序给需要析 型或客户端型中合适的一种作为将要被创 | 构的对象分配的内存块。MFC的new操作可以选择普通 建的对象的内存块类型。 |
| CRT内存块是CRT库为自 些块,所以在内存泄漏报告 | 己使用而分配的内存块。CRT在 中这种类型并不常见,除非发生 | 处理自己的释放内存操作时使用这 严重异常(例如:CRT库出错)。 |
| 还有两种类型你在内存泄漏信息中看不到: |
| 自由块,它是已经被释放的内存块; |
| 忽略块,它是已经被特殊标示的内存块。 |
| 设置CRT报告的格式: |
| 在默认情况下,_CrtDu 使用_CrtSetReportMode让 出信息到其他的地方,在这 _CRTDBG_MODE_DEBUG );语 | mpMemoryLeaks输出的内存泄漏 这些输出信息输出到其他地方。 种情况下,你可以使用_CrtSetR 句使输出信息重新定位到Output | 信息就象前边描述的那样。你可以 如果你使用一个库,它可能要使输 eportMode( _CRT_ERROR, 窗口。 |
| 根据内存分配编号设置断点: |
| 内存泄漏报告中的文件名和行数告诉 能找到问题所在。在一个运行的程序中一 只发生在其中的某次操作中。为了确认问 道发生泄漏的条件。内存分配编号使得解 后的大括弧凇@纾谏厦娴氖涑鲋小?br>泄漏发生在第18次分配操作中。 | 你内存泄漏的位置,但是知道内存泄漏位置不是总是 个内存分配操作可能被调用多次,但是内存泄漏可能 题所在,你除了知道泄漏的位置之外,你还必须要知 决这个问题成为可能。这个数字就在文件名、行数之 18”就是内存分配编号,它的意思是你程序中的内存 |
| CRT库对正在运行程序 (象MFC)。一个对象的分 通过代码被分配(在大多数 | 中所有的内存块分配进行计数, 配编号是n表示第n个对象被分配 情况下它们并不相同)。 | 包括自身的内存分配,或者其他库 ,但是它可能并不表示第N个对象 |
| 你可以根据内存分配编号在内存被分 断点,当你的程序在断点处停止后,你可 分配断点。在Watch窗口中的Name列中输 的CRT库的话你必须包含上挛淖?{,,m debugger处理这次调用,并且把返回值显 返回值是-1。在value列中输入你想设置 | 配的位置设置断点。先在程序开始部分附近设置一个 以通过QuickWatch对话框或者Watch窗口来设置内存 入_crtBreakAlloc,如果你使用的是多线程DLL版本 svcrtd.dll}_crtBreakAlloc。完成后按回车, 示在value列中。如果你没有设置内存分配断点的话 的分配数,例如18。 |
| 你在自己感兴趣的内存分配位置设置 序在相同的条件下,这样才能保证内存分 停下来后, 你可以查看Call 窗口和其他 必要你可以继续运行程序,看一看这个对 的释放。 | 断点后,你可以继续debugging。细心的运行你的程 配的顺序不致发生变化。当程序在特定的内存分配处 的debugger信息来分析此次内存分配的条件。如果有 象有什么变化,或许可以得知为什么内存没有被正确 |
| 尽管这个操作非常容易 行代码_crtBreakAlloc = 1 | ,但是如果你高兴的话也可以在 8;另外也可以通过_CrtSetBreak | 代码中设置断点。在代码中增加一 Alloc(18)来完成设置。 |
| 比较内存状态 |
| 另一个定位内存泄漏的 个结构体类型 _CrtMemStat | 方法是在重要位置捕捉应用程序 e,使用它你可以保存内存状态 | 的“内存快照”。CRT库提供了一 的快照(当前状态)。 |
| _CrtMemState s1, s2, s3; |
| 为了得到一个快照,可以把一个_Crt 个函数可以把当前的内存状态填充在结构 | MemState 结构体传给_CrtMemCheckpoint 函数,这 体中: |
| _CrtMemCheckpoint( &s1 ); |
| 你可以通过把结构体_CrtMemState 内容。 | 传给_CrtMemDumpStatistics函数来输出结构体中的 |
| _CrtMemDumpStatistics( &s3 );( &s1 ); |
| 它输出的信息如下: |
| 0 bytes in 0 Free Blocks. |
| 0 bytes in 0 Normal Blocks. |
| 3071 bytes in 16 CRT Blocks. |
| 0 bytes in 0 Ignore Blocks. |
| 0 bytes in 0 Client Blocks. |
| Largest number used: 3071 bytes. |
| Total allocations: 3764 bytes. |
| 为了得知一段代码中是 ,然后调用_CrtMemDiffere | 否有内存泄漏,你可以在这段代 nce函数来比较两个状态: | 码的开始和完成处分别拍一个快照 |
| _CrtMemCheckpoint( &s1 ); |
| // memory allocations take place here |
| _CrtMemCheckpoint( &s2 ); |
| if ( _CrtMemDifferen | ce( &s3, &s1, &s2) ) |
| _CrtMemDumpStatistics( &s3 ); |
| 就像名字中暗示的那样 一个参数)。把 _CrtMemCh 较结果,这也是一种检测内 rtMemCheckpoint把程序分 检查内存泄漏。 | ,_CrtMemDifference比较两个 eckpoint 放在程序的开始和结 存泄漏的方法。如果发现内存泄 成两半分别使用上述方法来检测 | 内存状态,并且产生一个结果(第 尾,调用_CrtMemDifference 来比 漏,你可以使用_C 内存泄漏,这样就是使用二分法来 |