在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1.msdn 在debug模式下的内存结构 (曾今在gaia引擎里看过类似的自己模仿实现的内存管理结构) typedef struct _CrtMemBlockHeader { // Pointer to the block allocated just before this one: struct _CrtMemBlockHeader *pBlockHeaderNext; // Pointer to the block allocated just after this one: struct _CrtMemBlockHeader *pBlockHeaderPrev; char *szFileName; // File name int nLine; // Line number size_t nDataSize; // Size of user block int nBlockUse; // Type of block long lRequest; // Allocation number // Buffer just before (lower than) the user's memory: unsigned char gap[nNoMansLandSize]; } _CrtMemBlockHeader;
/* In an actual memory block in the debug heap, * this structure is followed by: * unsigned char data[nDataSize]; * unsigned char anotherGap[nNoMansLandSize];//防止内存写越界 */
The “NoMansLand” buffers on either side of the user data area of the block are currently 4 bytes in size, and are filled with a known byte value used by the debug heap routines to verify that the limits of the user’s memory block have not been overwritten. The debug heap also fills new memory blocks with a known value. If you elect to keep freed blocks in the heap’s linked list as explained below, these freed blocks are also filled with a known value. Currently, the actual byte values used are as follows: NoMansLand (0xFD) The “NoMansLand” buffers on either side of the memory used by an application are currently filled with 0xFD. Freed blocks (0xDD) The freed blocks kept unused in the debug heap’s linked list when the _CRTDBG_DELAY_FREE_MEM_DF flag is set are currently filled with 0xDD. New objects (0xCD) New objects are filled with 0xCD when they are allocated.
msdn说明 The _CrtDumpMemoryLeaks function determines whether a memory leak has occurred since the start of program execution. When a leak is found, the debug header information for all of the objects in the heap is dumped in a user-readable form. When _DEBUG is not defined, calls to _CrtDumpMemoryLeaks are removed during preprocessing. _CrtDumpMemoryLeaks is frequently called at the end of program execution to verify that all memory allocated by the application has been freed. The function can be called automatically at program termination by turning on the _CRTDBG_LEAK_CHECK_DF bit field of the _crtDbgFlag flag using the _CrtSetDbgFlag function. _CrtDumpMemoryLeaks calls _CrtMemCheckpoint to obtain the current state of the heap and then scans the state for blocks that have not been freed. When an unfreed block is encountered, _CrtDumpMemoryLeaks calls _CrtMemDumpAllObjectsSince to dump information for all of the objects allocated in the heap from the start of program execution. By default, internal C run-time blocks (_CRT_BLOCK ) are not included in memory dump operations. The _CrtSetDbgFlag function can be used to turn on the _CRTDBG_CHECK_CRT_DF bit of _crtDbgFlag to include these blocks in the leak detection process. For more information about heap state functions and the _CrtMemState structure, see the Heap State Reporting Functions . For information about how memory blocks are allocated, initialized, and managed in the debug version of the base heap, see Memory Management and the Debug Heap . 应用: #include "stdafx.h" 不含红色部分输出: Detected memory leaks! 含红色部分输出: Detected memory leaks! -------------------------------------------------------------------------------------------
2.信息输出 内存管理是C++程序员的痛。我的《内存管理变革 》系列就是试图讨论更为有效的内存管理方式,以杜绝(或减少)内存泄漏,减轻C++程序员的负担。由于工作忙的缘故,这个系列目前未完,暂停。 class CMyApp : public CWinApp Detected memory leaks ! 呵呵,这不需要。其实,MFC也没有自己做。内存泄漏检测的工作是VC++的C运行库做的。也就是说,只要你是VC++程序员,都可以很方便地检测内存泄漏。我们还是给个样例: #include < crtdbg.h > inline void EnableMemLeakCheck() void main() Detected memory leaks ! 定位内存泄漏由于哪一句话引起的 一般我们首先确定内存泄漏是由于哪一句引起。在MFC中,这一点很容易。你双击内存泄漏报告的文字,或者在Debug窗口中按F4,IDE就帮你定位到申请该内存块的地方。对于上例,也就是这一句: int* leak = new int[10]; 这多多少少对你分析内存泄漏有点帮助。特别地,如果这个new仅对应一条delete(或者你把delete漏写),这将很快可以确认问题的症结。 我们前面已经看到,不使用MFC的时候,生成的内存泄漏报告与MFC不同,而且你立刻发现按F4不灵。那么难道MFC做了什么手脚? 其实不是,我们来模拟下MFC做的事情。看下例: inline void EnableMemLeakCheck() #ifdef _DEBUG void main() 再运行这个样例,你惊喜地发现,现在内存泄漏报告和MFC没有任何分别了。 快速找到内存泄漏 创建对象的地方是一个类工厂(ClassFactory)模式。很多甚至全部类实例由同一个new创建。对于此,定位到了new出对象的所在行基本没有多大帮助。 答:有。 在内存泄漏情况复杂的时候,你可以用以下方法定位内存泄漏。这是我个人认为通用的内存泄漏追踪方法中最有效的手段。 我们再回头看看crtdbg生成的内存泄漏报告: Detected memory leaks ! 其实,它代表了第几次内存分配操作。象这个例子,{52}代表了第52次内存分配操作发生了泄漏。你可能要说,我只new过一次,怎么会是第52次?这很容易理解,其他的内存申请操作在C的初始化过程调用的呗。:) 有没有可能,我们让程序运行到第52次内存分配操作的时候,自动停下来,进入调试状态?所幸,crtdbg确实提供了这样的函数:即 long _CrtSetBreakAlloc (long nAllocID)。我们加上它: inline void EnableMemLeakCheck() #ifdef _DEBUG void main() 当然,_CrtSetBreakAlloc 要求你的程序执行过程是可还原的(多次执行过程的内存分配顺序不会发生变化)。这个假设在多数情况下成立。不过,在多线程的情况下,这一点有时难以保证。
附加说明: /****************************************************************************************************************/ VC使用CRT调试功能来检测内存泄漏 作者:JerryZ C/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:“最大的长处也可能成为最大的弱点”,那么 C/C++ 应用程序正好印证了这句话。在 C/C++ 应用程序开发过程中,动态分配的内存处理不当是最常见的问题。其中,最难捉摸也最难检测的错误之一就是内存泄漏,即未能正确释放以前分配的内存的错误。偶尔发生的少量内存泄漏可能不会引起我们的注意,但泄漏大量内存的程序或泄漏日益增多的程序可能会表现出各种各样的征兆:从性能不良(并且逐渐降低)到内存完全耗尽。更糟的是,泄漏的程序可能会用掉太多内存,导致另外一个程序垮掉,而使用户无从查找问题的真正根源。此外,即使无害的内存泄漏也可能殃及池鱼。 幸运的是,Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法。下面请和我一起分享收获——如何使用 CRT 调试功能来检测内存泄漏? 一、如何启用内存泄漏检测机制 VC++ IDE 的默认状态是没有启用内存泄漏检测机制的,也就是说即使某段代码有内存泄漏,调试会话的 Output 窗口的 Debug 页不会输出有关内存泄漏信息。你必须设定两个最基本的机关来启用内存泄漏检测机制。 一是使用调试堆函数: #define _CRTDBG_MAP_ALLOC #include<crtdbg.h>
通过包含 crtdbg.h 头文件,可以将 malloc 和 free 函数映射到其“调试”版本 _malloc_dbg 和 _free_dbg,这些函数会跟踪内存分配和释放。此映射只在调试(Debug)版本(也就是要定义 _DEBUG)中有效。发行版本(Release)使用普通的 malloc 和 free 函数。#define 语句将 CRT 堆函数的基础版本映射到对应的“调试”版本。该语句不是必须的,但如果没有该语句,那么有关内存泄漏的信息会不全。 二是在需要检测内存泄漏的地方添加下面这条语句来输出内存泄漏信息: _CrtDumpMemoryLeaks(); 当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在 Output 窗口的 Debug 页中显示内存泄漏信息。比如: Detected memory leaks! Dumping objects -> C:\Temp\memleak\memleak.cpp(15) : {45} normal block at 0x00441BA0, 2 bytes long. Data: <AB> 41 42
c:\program files\microsoft visual studio\vc98\include\crtdbg.h(552) : {44} normal Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD
c:\program files\microsoft visual studio\vc98\include\crtdbg.h(552) : {43} normal Data: < C > 08 02 43 00 16 00 00 00 00 00 00 00 00 00 00 00
Object dump complete.
如果不使用 #define _CRTDBG_MAP_ALLOC 语句,内存泄漏的输出是这样的: Detected memory leaks! Dumping objects -> {45} normal block at 0x00441BA0, 2 bytes long. {44} normal block at 0x00441BD0, 33 bytes long. {43} normal block at 0x00441C20, 40 bytes long. Object dump complete.
根据这段输出信息,你无法知道在哪个源程序文件里发生了内存泄漏。下面我们来研究一下输出信息的格式。第一行和第二行没有什么可说的,从第三行开始: xx}:花括弧内的数字是内存分配序号,本文例子中是 {45},{44},{43};
C:\Temp\memleak\memleak.cpp(15)
使用 _CrtSetDbgFlag 设置 CRT 报告模式 _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG ); 有关使用 _CrtSetReportMode 的详细信息,请参考 MSDN 库关于 _CrtSetReportMode 的描述。 二、解释内存块类型 前面已经说过,内存泄漏报告中把每一块泄漏的内存分为 normal(普通块)、client(客户端块)和 CRT 块。事实上,需要留心和注意的也就是 normal 和 client,即普通块和客户端块。 除了上述的类型外,还有下面这两种类型的内存块,它们不会出现在内存泄漏报告中: 三、如何在内存分配序号处设置断点 在内存泄漏报告中,的文件名和行号可告诉分配泄漏的内存的代码位置,但仅仅依赖这些信息来了解完整的泄漏原因是不够的。因为一个程序在运行时,一段分配内存的代码可能会被调用很多次,只要有一次调用后没有释放内存就会导致内存泄漏。为了确定是哪些内存没有被释放,不仅要知道泄漏的内存是在哪里分配的,还要知道泄漏产生的条件。这时内存分配序号就显得特别有用 ——这个序号就是文件名和行号之后的花括弧里的那个数字。 例如,在本文例子代码的输出信息中,“45”是内存分配序号,意思是泄漏的内存是你程序中分配的第四十五个内存块: Detected memory leaks! Dumping objects -> C:\Temp\memleak\memleak.cpp(15) : {45} normal block at 0x00441BA0, 2 bytes long. Data: <AB> 41 42 ...... Object dump complete.
例如,在 Watch 窗口中,在 Name 栏键入下面的表达式: _crtBreakAlloc
{,,msvcrtd.dll}_crtBreakAlloc
用你想要在其位置中断的内存分配的分配序号替换 Value 栏中的值。例如输入 45。这样就会在分配序号为 45 的地方中断。 在所感兴趣的内存分配处设置断点后,可以继续调试。这时,运行程序时一定要小心,要保证内存块分配的顺序不会改变。当程序在指定的内存分配处中断时,可以查看 Call Stack(调用堆栈)窗口和其它调试器信息以确定分配内存时的情况。如果必要,可以从该点继续执行程序,以查看对象发生了什么情况,或许可以确定未正确释放对象的原因。 尽管通常在调试器中设置内存分配断点更方便,但如果愿意,也可在代码中设置这些断点。为了在代码中设置一个内存分配断点,可以增加这样一行(对于第四十五个内存分配): _crtBreakAlloc = 45;
_CrtSetBreakAlloc(45);
四、如何比较内存状态 定位内存泄漏的另一个方法就是在关键点获取应用程序内存状态的快照。CRT 库提供了一个结构类型 _CrtMemState。你可以用它来存储内存状态的快照: _CrtMemState s1, s2, s3;
_CrtMemCheckpoint( &s1 );
_CrtMemDumpStatistics( &s1 );
0 bytes in 0 Free Blocks.
_CrtMemCheckpoint( &s1 );// 获取第一个内存状态快照 // 在这里进行内存分配 _CrtMemCheckpoint( &s2 );// 获取第二个内存状态快照 // 比较两个内存快照的差异
尽管 VC ++ 具有一套专门调试 MFC 应用程序的机制,但本文上述讨论的内存分配很简单,没有涉及到 MFC 对象,所以这些内容同样也适用于 MFC 程序。在 MSDN 库中可以找到很多有关 VC++ 调试方面的资料,如果你能善用 MSDN 库,相信用不了多少时间你就有可能成为调试高手。
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论