分类
用VC++ debugger检测和隔离内存泄漏‖
debugger 和 CRT库提供了
一系列有效的检测和鉴定内存泄
漏的工具。
| 设置内存泄漏检测 |
| 检测内存泄漏的基本工 中你必须含有下面的说明: | 具是调试器和CRT调试堆函数。 | 为了使用调试堆函数,在你的程序 |
| #define _CRTDBG_MAP_ | ALLOC#include #include |
| #include说明必须按顺序说明。如果 crtdbg.h的_malloc_dbg和 _free_dbg将 的分配和释放。这种映射仅仅在一个测试 。释放的体系使用通常的malloc和 free | 改变了顺序,所用的函数可能不能正常工作。包含 malloc和free函数映射到测试版中,它可以跟踪内存 体系中发生(也就是说,仅仅当_DEBUG被定义的时候) 功能。 |
| #define说明映射CRT堆函数的低级版 有它,内存泄漏处含有的只是没有多大用 | 本到相应的测试版本。这个说明是不需要的,但是没 处的信息。 |
| 一旦你已经增加了刚才 : | 的说明,你能够通过在你的程序 | 中包含下面的说明来释放内存信息 |
| _CrtDumpMemoryLeaks(); |
| 当调试情况下运行程序时,在输出窗 泄漏的信息。内存泄漏信息类似下面这样 | 口的Debug 标签处_CrtDumpMemoryLeaks表现出内存 : |
| Detected memory leaks! |
| Dumping objects -> |
| C:PROGRAM FILESVIS normal block at | UAL STUDIOMyProjectsleakte | stleaktest.cpp(20) : {18} |
| 0x00780E80, 64 bytes long. |
| Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD |
| Object dump complete. |
| 如果你没有用#define | _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_ALLOC被定义时,_Cr _CRTDBG_MAP_ALLOC没有被定义,那么将 | tDumpMemoryLeaks给了你更多的有用信息。如果 向你如下显示: |
| 内存分配数值(花括号内) |
| 模块的类型(normal、client或者CRT) |
| 以十六进制格式定位的内存 |
| 以字节计模块的大小 |
| 第一个十六字节的内容(也可以用十六进制) |
| 当定义了_CRTDBG_MAP_ALLOC的时候 的文件。在文件名之后括号内的数字(20 数值和文件名的输出行, | ,显示的内容也向你展现了出现泄漏内存所分配地方 ,以此为例)是文件内的行数值。如果你双击包含行 |
| C:PROGRAM FILESVIS normal block at | UAL STUDIOMyProjectsleakte | stleaktest.cpp(20) : {18} |
| 0x00780E80, 64 bytes long. |
| 指针将会跳到源文件中 )。选择输出行并按F4将有 | 内存被分配地方的行(在上面的 同样的效果。 | 情况下,leaktest.cpp的行号为20 |
| 使用_CrtSetDbgFlag |
| 如果你的程序总是在同一各地方存在 是,如果你的程序需要在多个位置退出该 _CrtDumpMemoryLeaks,你可在你的程序 | ,那么调用_CrtDumpMemoryLeaks时非常容易的。但 怎么办?在每一个可能的出口处如果不调用 开始处包含下面的调用: |
| _CrtSetDbgFlag( _CRT | DBG_ALLOC_MEM_DF | _CRTDBG_L | EAK_CHECK_DF); |
| 当程序退出时,这个说 _CRTDBG_ALLOC_MEM_DF和 _ | 明自动地调用_CrtDumpMemoryLe CRTDBG_LEAK_CHECK_DF。 | aks。你必须设置两个位域, |
| 翻译内存模块的类型 |
| 内存泄漏信息鉴别泄漏内存的每一个 CRT模块。实际上,普通的模块和客户模 | 模块作为一个普通的模块、一个客户模块或者一个 块是你可能留心的唯一类型。 |
| 一个普通模块(normal block)是由你的程序分配的普通内存。 |
| 一个客户模块(client 被Microsoft Foundation C 客户模块,来适合被创建的 | block)是一种特殊的内存模块, lasses (MFC)所使用。MFC new 模块。 | 它由于需要一个析构函数的对象而 操作子建立一个普通模块或者一个 |
| 一个CTR模块是由CRT库提供自己使用 去分配,因此你不可能在内存泄漏报告中 CRT库崩溃)。 | 而分配的内存模块。CRT库对这些模块来管理自己的 注意到这些,除非有些地方有严重的错误(例如, |
| 在内存泄漏信息中有两种你从来没有见过的模块类型: |
| 空闲模块(free block)是一种被释放的内存模块 |
| Ignore block是你已经 | 特殊标记过以至于在内存泄漏报 | 告中不会出现的模块。 |
| 设置CRT报告样式 |
| 像以前的一样,按默认 Debug窗格。你可以运用_Cr 一个库,它可能重新设置输 出位置回到输出窗口: | 方式,_CrtDumpMemoryLeaks倾 tSetReportMode重新设置它到堆 出到另一个位置。在这种情况下 | 卸内存泄漏信息到输出窗口的 存处,到另一个位置。如果你使用 ,你能够利用下面的说明来设置输 |
| _CrtSetReportMode( _CRT_ERROR, _ | CRTDBG_MODE_DEBUG ); |
| 关于使用_CrtSetRepor _CrtSetReportMode节。 | tMode去发送输出信息到另一个 | 位置,要看Visual C++文件的 |
| 在内存分配数目处设置一个断点 |
| 在内存泄漏报告中的文件名和行号可 里分配对于鉴定问题不总是充分的。在一 次,但是它可能在某次调用中泄漏内存。 配,还要知道泄漏发生的条件。对你来说 显示的时候,文件名和行号之后,这是在 ,"18"是内存分配号。它的意思是泄漏的 | 告诉你泄漏的内存在那里被分配,但是了解内存在那 个程序运行过程中,经常是一个分配将会被调用很多 为了确定问题,你必须不但知道泄漏的内存在那里分 ,使它成为可能的那条信息是内存分配号。当那些被 curly brace中出现的数值。例如,在下面的输出中 内存是你程序中内存分配的第十八个模块。 |
| Detected memory leaks! |
| Dumping objects -> |
| C:PROGRAM FILESVISUAL STUDIOM normal block at | yProjectsleaktestleaktest.cpp(20) : {18} |
| 0x00780E80, 64 bytes long. |
| Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD |
| Object dump complete. |
| CRT库计算在程序运行 其它模块。因此带有分配号 码分配的第n个对象。(在 | 期间分配的所用内存模块,包括 n的一个对象是在你的程序中分 大部分情况下,它是不会的。) | CRT自己分配的内存或者诸如MFC的 配的第n个对象,但不可能是由代 |
| 你可以利用分配号在内 始很近处,设置一个位置断 Watch窗口设置这样一个位 | 存分配的地方设置一个断点。为 点。当你的程序在那一点暂停时 置断点。例如,在Watch窗口?br> | 了做这些,你可以距离你的程序开 ,你能够从QuickWatch对话框或者 ,在Name栏键入下面的表达式: |
| _crtBreakAlloc |
| 如果你正在用CRT库的多线程的dynam 作符,像这里说明的: | ic-link library (DLL)版本,你必须含有上下文操 |
| {,,msvcrtd.dll}_crtBreakAlloc |
| 现在,按RETURN。调试器评估调用并 还没有设置任何断点,那么这个值是-1 表中的值--例如,18 去中断早期在输出 | 且把结果放置在Value栏。如果你在内存分配过程中 。使用你想中断处内存分配的分配数值来代替Value 过程中展现的分配。 |
| 当你在你感兴趣的内存 运行程序时一定要小心,因 的时候,你能够查看Call S 要的话,你可以继续从那一 为了没有正确地被去分配。 | 分配处设置断点之后,你能够继 而分配的顺序不会改变。当你的 tack窗口和其他的测试信息来确 点执行程序,以至于了解对象到 (对对象设置一个数据断点是很 | 续调试。在与从前相同的条件下, 程序在一个特殊的内存分配点中断 定在此条件下内存的分配。如果需 底发生了什么事,同时还可能确定 有帮助的。) |
| 虽然在调试器中设置内 码中设置它们。为了在你的 内存分配): | 存分配断点通常更加容易,但是 代码中设置一个内存分配断点, | 如果你喜欢的话,你可以在你的代 可以增加这样一行(对于第十八个 |
| _crtBreakAlloc = 18; |
| 最为一个选择,你可以使用有相同效果的_CrtSetBreakAlloc函数。 |
| _CrtSetBreakAlloc(18); |
| 比较内存状态 |
| 定位内存泄漏的另一个 个结构类型,_CrtMemState | 方法就是在关键点对应用程序的 。你可以使用它来存储内存状态 | 内存状态做快照。CRT库提供了一 的一个快照。 |
| _CrtMemState s1, s2, s3; |
| 为了在特定点对内存状 _CrtMemCheckpoint函数。 | 态进行快照,可以传递一个_Crt 此函数用当时内存状态的一个快 | MemState结构到he 照来填充此结构: |
| _CrtMemCheckpoint( &s1 ); |
| 你可以通过传递此结构 的内容: | 到_CrtMemDumpStatistics函数 | 来倾卸_CrtMemState结构的任意点 |
| _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. |
| 为了确定一个内存泄漏是否在一节代 照,然后用_CrtMemDifference比较两种 | 码中出现,你可以在此节前和此节后对内存状态作快 状态: |
| _CrtMemCheckpoint( &s1 ); |
| // memory allocations take place here |
| _CrtMemCheckpoint( &s2 ); |
| if ( _CrtMemDifferen | ce( &s3, &s1, &s2) ) |
| _CrtMemDumpStatistics( &s3 ); |
| 像名字暗示的一样,_C 一个不同于这两个状态的结 rtMemCheckpoint调用和使 法。如果一个泄漏被检测到 二元binary search techni | rtMemDifference比较两个内存 果(第三个参数)。在你的程序 有_CrtMemDifference来比较结 ,那么可以使用_CrtMemCheckpo que来定位泄漏。 | 状态(最先的两个参数)并且产生 开始和结尾处的_C 果为检测内存泄漏提供了另一种方 int调用来分割你的程序并且使用 |