dedecms织梦内容管理系统    
首页 | java | C/C++ | PHP | 操作系统 | ajax | 脚本编程 | 安全技术 | 本站下载页 | flex | CRM | 专题 | QQ群 | 测试中心 | 会员中心 | 积分规则
  当前位置:主页>C/C++>C/C++技术资料>文章内容
处理内存泄漏的一种MFC方法
来源:信息产业部数据所     作者:    

Windows使用复杂的内存管理器控制和优化内存的使用(包括磁盘缓冲)。一旦内存管理出现纰漏就会导致内存泄漏。内存泄漏的实质一般是因为在堆上分配了某块内存但以后不再对其重新分配,使得该部分内存失去重用性。出现这一问题的多数应用程序一开始往往正常运行,所以要检测出该类问题是较为困难的。不过,要将其找出并得到正确的处理才更麻烦。大多数MFC应用程序允许Windows安全地管理分配给资源的内存,如果分配内存的组件不由系统所处理的话内存泄漏的危险就大大增加了。这里通过举例来讨论一些相关的问题。

示例:多次重绘窗口导致内存泄漏

我们简单建立一个STD的MFC工程MLeak,该程序首先创建逻辑字体,随后TextOut() 函数在窗口的客户区书写文本,如果程序类似图1(略)左那样持续再长时间你也看不到会出现什么奇怪的现象。但你用鼠标抓住窗口的边界改变窗口大小多次(多的时候要到数十次)就会看见窗口变成了图1右那样:字体出问题了。TextOut()函数仍然可以在窗口上书写文本,但是逻辑字体却没有得到正确的创建。一般会认为问题出在OnDraw()内的字体创建过程中。真是这样吗?

查找和分析问题

幸好有些MFC类和函数可以用于发现内存泄漏。添加相应代码就有助于检查CMLeakView类中存在的内存泄漏问题(关键的代码以粗体标识)。首先我们用ClassWizard为视图类加入 OnCreate() 函数,目的是为了在程序初始化时获得堆的有关统计数据。只要调用oldMemState.Checkpoint()函数即可做到这一点。接着OnDraw()内在完成与字体有关的全部工作后将执行以下附加的调试代码:

#ifdef _DEBUG
newMemState.Checkpoint();
if(diffMemState.Difference
(oldMemState, newMemState))
 {
TRACE("Difference between first and now!\n\n");
diffMemState.DumpStatistics();
  }
#endif

调用newMemState.Checkpoint() 将获得堆的最新情况,diffMemState.Difference()则在原始值和当前值出现差异时返回信息。统计结果通过调用diffMemState.DumpStatistics()被扔出。因为该信息包含在OnDraw()函数内,而OnDraw()响应WM_PAINT消息重绘屏幕窗口,则在每次改变窗口大小时将打印出统计结果,我们发现每次公布的统计数据的最后一行才有变化:
Difference between first and now!
(第一次统计信息的开始行)
… …
Total allocations: 87 bytes.
(第一次统计信息的结束行)
……
Total allocations: 132 bytes.
(第二次统计信息的结束行)
   … …
Total allocations: 14352 bytes.
(最后一次统计信息的结束行)

可以注意到每次重绘屏幕都导致整个分配区在增加,增加幅度为45字节,重绘一定次数后内存分配就到达了14,352字节。那么会不会是忘了为逻辑字体结构分配内存呢?我们再向OnDraw()中插入以下粗体代码:
  LOGFONT lf;
  … …
  memset(&lf,0,sizeof(LOGFONT));
… …

结果如故,说明逻辑字体结构大小并没有与此发生必然联系。从OnDraw()中去掉字体创建过程并加入到OnCreate()中,使逻辑字体资源在创建窗口时得到创建,不过还是可以发现分配的整个内存仍然持续增加!于是修改OnDraw()如下:
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
}

问题仍然出现,而以下代码是OnDraw()中所增加的唯一代码:
  pDC- >TextOut
(20, 200, "This program has memory problems");

将该行代码注释掉并重新编译、运行诊断程序。可以发现整个内存分配统计结果增幅为0。看来,分配给字符串的内存在屏幕每次重绘时被重新分配了。
内存诊断参数

启用或禁用内存诊断可以调用全局函数AfxEnableMemoryTracking()。Debugger将自动地控制它,所以该函数作为开关将显著增加程序执行速度并减少诊断信息。MFC全局变量afxMemDF则使得特定内存诊断特性可用。该变量信息可以查阅相关资料。

查找内存泄漏

我们首先实现一个CMemoryState对象(CMemoryState的使用可参看有关资料)。在输入有问题代码之前调用Checkpoint()函数 来获得内存使用的原始情况。然后实现另一个CMemoryState对象并在写完有问题代码之后调用Checkpoint()函数来得到内存使用后的情况。当然,还可以实现第三个CMemoryState对象并调用Difference()成员函数。调用该函数时用先前的两个CMemoryState对象作为其参数。如果内存前后没有差异则返回值非0。这样至少可以说明是否某些内存块还没有释放。以下是使用这三个对象的部分代码:

#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

[1]  

 

 

上一篇:VC++的链接错误LNK2001   下一篇:Win32 核心 DPC 设计思想和实现思路浅析
[收藏] [推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
用户名: 新注册) 密码: 匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论
  热点文章
·C语言数组排序小结
·Python 与 C++ 程序的简单实例对
·c++ 数组与指针
·C++中的内存管理(new、delete、
·如何用C语言开发DSP嵌入式系统
·如何用C语言开发DSP嵌入式系统
·Linux下C语言编程
·学习arm的话先看哪本书?
·C++初学者应该关心的优秀图书一
·利用C语言小程序来解决大问题
·用C语言写PHP扩展的步骤
·用C语言扩展PHP功能
  相关文章
·VC++的链接错误LNK2001
·Win32 核心 DPC 设计思想和实现
·深度解析VC中的消息传递机制(下
·VC++5.0下MIDI、WAV及CD的播放
·深度解析VC中的消息传递机制(上
·经验总结之防止窗口闪烁的方法
·实现带阴影弹出的窗口
·ado方式访问带密码数据库的方法
·各种排序算法
·显示内存中的HTML内容
·利用钩子实现菜单阴影效果
·让你的控制台程序也支持自动补齐
  相关信息
copy right @ 百家拳软件项目研究室 2007 辽ICP备07011763