内存泄露检测之MFC

本文介绍了如何利用crtdbg工具快速定位MFC工程中的内存泄漏,包括通过输出窗口定位内存泄漏、使用_CrtSetBreakAlloc函数精确定位内存泄漏发生的具体行号以及在内存分配序号特定位置自动触发调试状态的方法,适用于解决复杂内存泄漏场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载请注明出处:http://blog.youkuaiyun.com/yf210yf/article/details/8024816

第一种:通过"OutPut窗口"定位引发内存泄漏的代码

例如:

在主对话框类中加入测试代码

void CMyDlg::mytest()
{
int* leak = new int[10];
}

调试运行后

在output窗口中可以看到内存泄露信息

Detected memory leaks!
Dumping objects ->
g:\c++\mfc\内存泄露检测(mfc版)\内存泄露检测(mfc版)\内存泄露检测(mfc版)\cmydlg.cpp(94) : {140} normal block at 0x00669270, 40 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

上述信息给出了泄露的定位。

但是如何在其他类中再次内存泄露,则不会给出具体位置。

如:

先建test类:

#include "StdAfx.h"
#include "test.h"


test::test(void)
{
testlink();
}


test::~test(void)
{
}


void test::testlink()
{
int* leak = new int[10];
}


在主对话框中声明对象,并测试:

void CMyDlg::mytest()
{
int* leak = new int[10];
test *test1=new test;
delete test1;
}


得到了内存泄露信息,但是在test类中的内存泄露则没有信息定位:

Detected memory leaks!
Dumping objects ->
{142} normal block at 0x00409318, 40 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
g:\c++\mfc\内存泄露检测(mfc版)\内存泄露检测(mfc版)\内存泄露检测(mfc版)\cmydlg.cpp(94) : {140} normal block at 0x00409270, 40 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.


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

加入


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


例如:

更改test类

#include "StdAfx.h"
#include "test.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


test::test(void)
{
testlink();
}


test::~test(void)
{
}


void test::testlink()
{
int* leak = new int[10];
}


然后再次调试运行

内存泄露的位置就有定位了

Detected memory leaks!
Dumping objects ->
g:\c++\mfc\内存泄露检测(mfc版)\内存泄露检测(mfc版)\内存泄露检测(mfc版)\test.cpp(19) : {142} normal block at 0x00409318, 40 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
g:\c++\mfc\内存泄露检测(mfc版)\内存泄露检测(mfc版)\内存泄露检测(mfc版)\cmydlg.cpp(94) : {140} normal block at 0x00409270, 40 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

转载请注明出处:http://blog.youkuaiyun.com/yf210yf/article/details/8024816


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

单确定了内存泄漏发生在哪一行,有时候并不足够。特别是同一个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 0x003C441040 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)。我们加上它:


你发现,程序运行到 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的地方,定位还是很准的,开始我怀疑,但最后检查果然是这地方没释放)会定位到错误行。


转载请注明出处:http://blog.youkuaiyun.com/yf210yf/article/details/8024816



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值