VC内存泄漏检测:三行代码快速定位泄漏点

检测维修 0 60

启用内存泄漏检测

用于检测内存泄漏的, 乃是针对C/c++ 调试器以及C运行时库 (CRT) 的主要工具, 也就是调试堆函数。

假若想要开启调试堆的每一函数, 于c + +程序里头, 依照以下顺序包纳以下语句:

#define _CRTDBG_MAP_ALLOC  
#include   
#include   

将CRT堆函数的基础版本, 通过#define语句, 映射到对应的调试版本。要是您忽略#define语句, 那么就是为了内存泄漏转储。

涵盖crtdbg.h, 把malloc以及free映射至其调试版本内, 存在函数_malloc_dbg与_free_dbg, 它们会对内存分配和解除分配予以跟踪。此映射仅仅在包含_DEBUG的调试版本时才会出现。发布版本运用普通的malloc和free函数。

经启用借助上述语句的调试堆函数后, 在应用程序退出之际, 于调用_CrtDumpMemoryLeaks之前, 会显示内存泄漏报告的应用程序退出点。

_CrtDumpMemoryLeaks();  

要是您的那种应用程序拥有好几个退出的状况, 并不需要依靠自己手动去设置_CrtDumpMemoryLeaks在每一个退出的地方哟。要是想要达成自动去调用_CrtDumpMemoryLeaks在每一个退出的地方, 那就得调用_CrtSetDbgFlag运用往下所展示的位域在应用程序的起始部位呢:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );  

一般情况下, _CrtDumpMemoryLeaks会把内存泄漏报告, 输出至“输出”窗口里的“调试”窗格当中。要是采用库, 那该库或许会将输出重新设置到别的地方。

能够运用_CrtSetReportMode把该报告进行重定向, 使其到别的地方, 或者再返回到输出窗口, 就如同下面所展示的如此这般:

_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );  

解释内存泄漏报告

若应用未对_CRTDBG_MAP_ALLOC加以定义, 那么_CrtDumpMemoryLeaks会呈现出如下这般的内存泄漏报告:

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, 那么内存泄漏的报告呈现出如下这般的情形, 所呈现显示表现为是这样的:

Detected memory leaks!  
Dumping objects ->  
c:\users\username\documents\projects\leaktest\leaktest.cpp(20) : {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进行了定义, 一旦进行了定义, 内存泄漏报告就会呈现出: 。

内存块类型处于正常状态, 存在客户端, 或者是CRT。“普通块”乃是由程序所分配的进行普通用途的内存。“客户端块”是那种由MFC程序运用于急需析构函数的对象的具备特殊性质的类型的内存块。MFC new运算符依据正在被创建的对象的需求去创建普通块或客户端块。

“CRT块”其所指的乃是, 由CRT库出于自身应用需求而进行分配操作以获取的内存块, CRT库会针对这些经其分配出来的块, 实施相应的处理举措也即解除分配如此, 除非出现运用CRT库时遇见严重问题这种特殊情形, 否则CRT块不会于内存泄漏报告里呈现出来。

绝对不会于内存泄漏报告里呈现另外两类内存块类型。一块得以释放的块是已经被释放了的, 所以依据定义它是不会造成泄漏的内存。被忽略的块乃是被明确标记, 是要从内存泄漏报告内予以排除的。

以前的技术判断内存泄漏时使用标准CRTmalloc函数来进行内存分配, 若您的程序运用c++new运算符来分配内存, 然而, 您或许只能在_malloc_dbg内存泄漏报告里看到文件名以及行号位置的operator new调用。为了创建更具实用性的内存泄漏报告, 可以编写像下面展示的这样来报告进行分配的行的宏:

#ifdef _DEBUG
    #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
    // Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the
    // allocations to be of _CLIENT_BLOCK type
#else
    #define DBG_NEW new
#endif

此刻, 能够替换new运算符号运用DBG_NEW于代码里的宏。在调试版本当中, DBG_NEW所运用的全局重载operator new采用附带参数的块类型、文件以及行号。重载new调用_malloc_dbg记录的额外信息。内存泄漏报告展示文件名以及行号泄漏对象的分配位置。发行版本依旧使用默认值new。以下是技术的示例标点。

// debug_new.cpp
// compile by using: cl /EHsc /W4 /D_DEBUG /MDd debug_new.cpp
#define _CRTDBG_MAP_ALLOC
#include 
#include 
#ifdef _DEBUG
    #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
    // Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the
    // allocations to be of _CLIENT_BLOCK type
#else
    #define DBG_NEW new
#endif
struct Pod {
    int x;
};
void main() {
    Pod* pPod = DBG_NEW Pod;
    pPod = DBG_NEW Pod; // Oops, leaked the original pPod!
    delete pPod;
    _CrtDumpMemoryLeaks();
}

于Visual Studio其内运行此代码之际,调试器当中调用时, _CrtDumpMemoryLeaks生成的报表输出, 看上去类似这样: 有一个窗口。

Detected memory leaks!
Dumping objects ->
c:\users\username\documents\projects\debug_new\debug_new.cpp(20) : {75}
 normal block at 0x0098B8C8, 4 bytes long.
 Data: <    > CD CD CD CD 
Object dump complete.

此输出, 至第20行, 已存在泄漏分配要报告的debug_new.cpp。

Note

我们不建议去创建, 一个名为new的预处理器宏, 不建议去创建任何和其他语言关键字相关的东西。

内存分配编号上设置断点

如果有分配了泄漏内存的块, 那么内存分配编号会对你进行通知, 块内存分配编号比如说为18, 这是在内存的应用程序运行之际分配的第18块, CRT报告涵盖运行阶段, 这里面包含了像是CRT库以及MFC等其他库在运行期间针对分配所有内存块的分配情形, 所以, 内存分配块编号18有可能并非是分配你代码的第18个内存块。

vc 内存泄漏检测工具_C/C++内存泄漏检测__CrtDumpMemoryLeaks调试堆函数

可以使用分配编号在内存分配位置设置断点。

若要设置使用监视窗口的内存分配断点:

您的应用程序的起点附近设置断点并开始调试。

当应用程序于断点之处出现暂停情况时, 将Watch予以打开, 借助选择窗口这种方式来调试, 具体做法是选择窗口调试, 再选择视窗这一项的Windows, 接着选择监视里面的1(又或是观看2, 观看3, 亦或是观看4)。

身处中Watch窗口之内, 输入_crtBreakAlloc里的名称列。

要是您运用的是多线程的 DLL 格式的 CRT 库, 也就是那个(/MD 选项), 那么就去添加上下文运算符: {,,ucrtbased.dll}_crtBreakAlloc。

按 Enter。

调试器会对计算调用予以执行, 然后把得出的结果放置到 “值”列当中。倘若你还未曾在内存分配那里设置任何断点, 那么此值将会是 -1。

以中值列出, 将值替换成, 用于想调试程序执行中断的内存分配的那个分配编号。

在内存分配编号那儿设置断点之后, 接着进行调试。要保证在相同状况下运行, 所以不会变动内存分配编号。当应用程序在指定的内存分配地方中断的时候, 借助调用堆栈窗口以及其他调试器窗口去弄清楚分配内存之际的情形。随后, 能够继续让程序执行来留意对象会出现啥状况, 并且弄明白为啥它没有正确释放。

有可能在对象之上设置数据断点后会有一定帮助。如若想了解详细的相关信息, 那么请去查阅使用断点这一设置。

你也可以在代码中设置内存分配断点。 您可以设置:

_crtBreakAlloc = 18;  

或:

_CrtSetBreakAlloc(18);  

比较内存状态

对于定位内存泄漏而言, 存在着另外一种技术, 该技术涉及到于关键点之处, 针对应用程序的内存状态去拍摄快照, 若想要在应用程序里某一给定的点的内存状态获取快照, 那就需要创建_CrtMemState结构, 并且将这个结构传递给_CrtMemCheckpoint函数。

_CrtMemState s1;  
_CrtMemCheckpoint( &s1 );  

_CrtMemCheckpoint函数, 将在该结构里, 填充当前内存状态的快照。

要是想要输出的内容是_CrtMemState结构, 那就得把它传递到_CrtMemDumpStatistics函数当中:

_CrtMemDumpStatistics( &s1 );  

_ CrtMemDumpStatistics为你输出内存状态的转储, 情况是像下面所呈现的这样:

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 ( _CrtMemDifference( &s3, &s1, &s2) )  
   _CrtMemDumpStatistics( &s3 );  

_CrtMemDifference对内存状态里的s1以及s2予以比较, 进而返回结果之中的(s3), (s3)为s1和s2之间所存在的差异。

用于查找内存泄漏的一项技术, 需在您的应用程序开头运行_CrtMemCheckpoint, 同时于结尾运行, 之后调用_CrtMemDifference, 利用其来比较结果。倘使_CrtMemDifference显示存在内存泄漏, 那么可增添更多_CrtMemCheckpoint调用, 通过二进制搜索来划分程序, 直至您已将泄漏源隔离找出。

误报

_ CrtDumpMemoryLeaks, 要是库把内部分配标记成普通块, 而非CRT块或者客户端块, 那就可能错误地表明存在内存泄漏。在如此情形下, _ CrtDumpMemoryLeaks没办法区分用户分配以及内部库分配。要是在_ CrtDumpMemoryLeaks调用点之后运行库分配的全局析构函数, 那么每个内部库分配都会被报告成内存泄漏。版本早于, 可能会致使, Visual Studio.NET 的标准模板库_CrtDumpMemoryLeaks, 用以报告此类的假正值。

请参阅

CRT 调试堆详细信息

调试器安全

调试本机代码

相关推荐: