📄 csdn_文档中心_处理内存泄漏的一种mfc方法.htm
字号:
OnCreate()
函数,目的是为了在程序初始化时获得堆的有关统计数据。只要调用oldMemState.Checkpoint()函数即可做到这一点。接着OnDraw()函数内在完成与字体有关的全部工作后将执行以下附加的调试代码:
<PRE>#ifdef _DEBUG
newMemState.Checkpoint();
if(diffMemState.Difference
(oldMemState, newMemState))
{
TRACE("Difference between first and now!\n\n");
diffMemState.DumpStatistics();
}
#endif
</PRE><FONT color=#ffffff>----</FONT> 调用newMemState.Checkpoint()
将获得堆的最新情况,diffMemState.Difference()则在原始值和当前值出现差异时返回信息。统计结果通过调用diffMemState.DumpStatistics()被扔出。因为该信息包含在OnDraw()函数内,而OnDraw()函数响应WM_PAINT消息重绘屏幕窗口,则在每次改变窗口大小时将打印出统计结果,我们发现每次公布的统计数据的最后一行才有变化:
<PRE>Difference between first and now!
(第一次统计信息的开始行)
… …
Total allocations: 87 bytes.
(第一次统计信息的结束行)
……
Total allocations: 132 bytes.
(第二次统计信息的结束行)
… …
Total allocations: 14352 bytes.
(最后一次统计信息的结束行)
</PRE><FONT color=#ffffff>----</FONT>
可以注意到每次重绘屏幕都导致整个分配区在增加,增加幅度为45字节,重绘一定次数后内存分配就到达了14,352字节。那么会不会是忘了为逻辑字体结构分配内存呢?我们再向OnDraw()函数中插入以下粗体代码:
<PRE> LOGFONT lf;
… …
memset(&lf,0,sizeof(LOGFONT));
… …
</PRE><FONT color=#ffffff>----</FONT>
结果如故,说明逻辑字体结构大小并没有与此发生必然联系。从OnDraw()函数中去掉字体创建过程并加入到OnCreate()中,使逻辑字体资源在创建窗口时得到创建,不过还是可以发现分配的整个内存仍然持续增加!于是修改OnDraw()如下:
<PRE>void CMLeakView::OnDraw(CDC* pDC)
{
CMLeakDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->TextOut(20, 200,
"This program has memory problems");
#ifdef _DEBUG
newMemState.Checkpoint();
if(diffMemState.Difference
(oldMemState, newMemState)) {
TRACE("Difference between first and now!\n\n");
diffMemState.DumpStatistics();
}
#endif
}
</PRE><FONT color=#ffffff>----</FONT>
问题仍然出现,而以下代码是OnDraw()中所增加的唯一代码: <PRE> pDC- >TextOut
(20, 200, "This program has memory problems");
</PRE><FONT color=#ffffff>----</FONT>
将该行代码注释掉并重新编译、运行诊断程序。可以发现整个内存分配统计结果增幅为0。看来,分配给字符串的内存在屏幕每次重绘时被重新分配了。
<P><FONT color=#b00e>内存诊断参数 </FONT>
<P><FONT color=#ffffff>----</FONT>
启用或禁用内存诊断可以调用全局函数AfxEnableMemoryTracking()。Debugger将自动地控制它,所以该函数作为开关函数将显著增加程序执行速度并减少诊断信息。MFC全局变量afxMemDF则使得特定内存诊断特性可用。该变量信息可以查阅相关资料。
<P><FONT color=#b00e>查找内存泄漏 </FONT>
<P><FONT color=#ffffff>----</FONT>
我们首先实现一个CMemoryState对象(CMemoryState的使用可参看有关资料)。在输入有问题代码之前调用Checkpoint()函数
来获得内存使用的原始情况。然后实现另一个CMemoryState对象并在写完有问题代码之后调用Checkpoint()函数来得到内存使用后的情况。当然,还可以实现第三个CMemoryState对象并调用Difference()成员函数。调用该函数时用先前的两个CMemoryState对象作为其参数。如果内存前后没有差异则函数返回值非0。这样至少可以说明是否某些内存块还没有释放。以下是使用这三个对象的部分代码:
<PRE>#ifdef _DEBUG
CMemoryState oldMemState,
newMemState, diffMemState;
oldMemState.Checkpoint();
#endif
…
(被测试的代码)
…
#ifdef _DEBUG
newMemState.Checkpoint();
if(diffMemState.Difference
(oldMemState, newMemState))
{
TRACE("Memory Leaked Here:\n\n" );
}
#endif
</PRE>
<P><FONT color=#b00e>内存状况统计 </FONT>
<P><FONT color=#ffffff>----</FONT> CMemoryState()
成员函数可用于得到当前内存的统计资料或者两个内存对象状态的差异。此外还可用于查找堆上内存泄漏。以下代码使用了原始信息来检测当前的内存状态:
<PRE>TRACE("Current Memory Picture:\n\n" );
NewMemState.DumpStatistics();
</PRE><FONT color=#ffffff>----</FONT> 很容易获取先后内存状态的差异: <PRE>if( diffMemState.Difference
(oldMemState,newMemState))
{
TRACE( "Memory Leaked Here:\n\n");
diffMemState.DumpStatistics();
}
diffMemState.DumpStatistics()的示例输出如下:
0 bytes in 0 Free Blocks
2 bytes in 1 Object Blocks
50 bytes in 5 Non-Object Blocks
Largest number used: 76 bytes
Total allocations: 304 bytes
</PRE><FONT color=#ffffff>----</FONT> 以上代码第一行指示延迟释放的内存块数目。当afxMemDF
变量设置为delayFreeMemDF
时就会这样。第二行用于指示多少对象还存在于堆上。第三行指示多少非对象块(新分配的)被分配并且没有被释放。第四行指示应用程序在给定时间内使用的最大内存。最后一行指示工程使用的全部内存。以上任何一行出现问题都意味着内存泄漏了。
<P><FONT color=#b00e>修复工程 </FONT>
<P><FONT color=#ffffff>----</FONT>
虽然在CMLeakView类中适当处理OnDraw()中的字符串也可能成功解决先前问题,不过AppWizard已经创建了负责存储和分配工作的专门类CMLeakDoc文档类。
我们可以将要显示的字符串在MLeakDoc.h文件中声明为CMLeakDoc的成员变量: <PRE>CString myCString;
然后在在CMLeakDoc的构造函数中对其赋值:
CMLeakDoc::CMLeakDoc()
{
myCString = "This program doesn't have a leak";
}
最后修复的工程文件大致如下所示:
// MLeakView.cpp :
implementation of the CMLeakView class
//
… …
CFont NFont;
… …
void CMLeakView::OnDraw(CDC* pDC)
{
… …
CFont* pOFont;
pOFont = pDC- >SelectObject(&NFont);
pDC- >TextOut(20, 200, pDoc- >myCString);
DeleteObject(pOFont);
}
… …
int CMLeakView::OnCreate
(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
LOGFONT lf;
memset(&lf,0,sizeof(LOGFONT));
lf.lfHeight = 50;
lf.lfWeight=FW_NORMAL;
lf.lfEscapement=0;
lf.lfOrientation=0;
lf.lfItalic=false;
lf.lfUnderline = false;
lf.lfStrikeOut = false;
lf.lfCharSet=ANSI_CHARSET;
lf.lfPitchAndFamily=34; //Arial
NFont.CreateFontIndirect(&lf);
return 0;
}
</PRE><FONT color=#ffffff>----</FONT>
以上的一些技术性的手段可以使程序员对一些很隐蔽的内存陷阱有一些新的认识,不过,发现并能解决内存泄漏问题始终是个需要耐心和细心的过程,经验或许会更重于技术指南。<BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE align=center bgColor=#006699 border=0 cellPadding=0 cellSpacing=0
width=770>
<TBODY>
<TR bgColor=#006699>
<TD align=middle bgColor=#006699 id=white><FONT
color=#ffffff>对该文的评论</FONT></TD>
<TD align=middle>
<SCRIPT src="CSDN_文档中心_处理内存泄漏的一种MFC方法.files/readnum.htm"></SCRIPT>
</TD></TR></TBODY></TABLE>
<TABLE align=center bgColor=#666666 border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TD bgColor=#cccccc colSpan=3><SPAN style="COLOR: #cccccc"><IMG height=16
hspace=1 src="CSDN_文档中心_处理内存泄漏的一种MFC方法.files/ico_pencil.gif" width=16>
</SPAN> zgrong <I>(2000-11-29 22:30:29)</I> </TD></TR>
<TR>
<TD bgColor=#ffffff colSpan=3 width=532><BR>1.该文章实际上是从《C++程序调试,电子工业出版社》上抄的
2.该方法实际上报告的可能的是虚假情况。在VC6.0中,将OnDraw中的pdc->TextOut(20,200,“This program
has memeory problems“);代码改成 CString str(“This program has memeory
problems“); pdc->TextOut(20,200,str); 然后按上述方法检查,同样发现Total
Allocation不断增加。造这种说法,字符串常量就不能在OnDraw中使用了。其次,假如你进行文件操作,如打开文件,再关闭,再用该方法检查,就会发现“内存泄漏”十分巨大!?
3.该方法实际上只对自己在堆上分配的内存进行一些检查,现在实际上没什么用。现在一般用NEW生成的若没Delete掉,DEBUG版本最后退出会告诉你的。而对其他内存,如BSTR所用内存泄露,SysAllocString分配却没有SysFreeString释放;以及COM用全局内存泄露,如对接口函数调用的[out]参数所用内存没释放,却根本无能为力。
4.建议本文章撤掉,不要再发表这种错误并抄袭的技术文章。 <BR></TD></TR></TBODY></TABLE><BR>
<DIV align=center>
<TABLE align=center bgColor=#cccccc border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TH bgColor=#006699 id=white><FONT
color=#ffffff>我要评论</FONT></TH></TR></TBODY></TABLE></DIV>
<DIV align=center>
<TABLE border=0 width=770>
<TBODY>
<TR>
<TD>你没有登陆,无法发表评论。 请先<A
href="http://www.csdn.net/member/login.asp?from=/Develop/read_article.asp?id=1624">登陆</A>
<A
href="http://www.csdn.net/expert/zc.asp">我要注册</A><BR></TD></TR></TBODY></TABLE></DIV><BR>
<HR noShade SIZE=1 width=770>
<TABLE border=0 cellPadding=0 cellSpacing=0 width=500>
<TBODY>
<TR align=middle>
<TD height=10 vAlign=bottom><A
href="http://www.csdn.net/intro/intro.asp?id=2">网站简介</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=5">广告服务</A> - <A
href="http://www.csdn.net/map/map.shtm">网站地图</A> - <A
href="http://www.csdn.net/help/help.asp">帮助信息</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=2">联系方式</A> - <A
href="http://www.csdn.net/english">English</A> </TD>
<TD align=middle rowSpan=3><A
href="http://www.hd315.gov.cn/beian/view.asp?bianhao=010202001032100010"><IMG
border=0 height=48 src="CSDN_文档中心_处理内存泄漏的一种MFC方法.files/biaoshi.gif"
width=40></A></TD></TR>
<TR align=middle>
<TD vAlign=top>百联美达美公司 版权所有 京ICP证020026号</TD></TR>
<TR align=middle>
<TD vAlign=top><FONT face=Verdana>Copyright © CSDN.net, Inc. All rights
reserved</FONT></TD></TR>
<TR>
<TD height=15></TD>
<TD></TD></TR></TBODY></TABLE></DIV>
<DIV></DIV><!--内容结束//--><!--结束//--></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -