内存管理器控制

转载 2004年06月23日 15:06:00

信息产业部数据所
廖 铮

---- 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 

内存状况统计

---- CMemoryState() 成员函数可用于得到当前内存的统计资料或者两个内存对象状态的差异。此外还可用于查找堆上内存泄漏。以下代码使用了原始信息来检测当前的内存状态:

TRACE("Current Memory Picture:/n/n" ); NewMemState.DumpStatistics(); 
---- 很容易获取先后内存状态的差异:
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 
---- 以上代码第一行指示延迟释放的内存块数目。当afxMemDF 变量设置为delayFreeMemDF 时就会这样。第二行用于指示多少对象还存在于堆上。第三行指示多少非对象块(新分配的)被分配并且没有被释放。第四行指示应用程序在给定时间内使用的最大内存。最后一行指示工程使用的全部内存。以上任何一行出现问题都意味着内存泄漏了。

修复工程

---- 虽然在CMLeakView类中适当处理OnDraw()中的字符串也可能成功解决先前问题,不过AppWizard已经创建了负责存储和分配工作的专门类CMLeakDoc文档类。 我们可以将要显示的字符串在MLeakDoc.h文件中声明为CMLeakDoc的成员变量:

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; } 
---- 以上的一些技术性的手段可以使程序员对一些很隐蔽的内存陷阱有一些新的认识,不过,发现并能解决内存泄漏问题始终是个需要耐心和细心的过程,经验或许会更重于技术指南。

为 C/C++ 项目构建您自己的内存管理器

原文见链接为 C/C++ 项目构建您自己的内存管理器 原文见链接c语言内存管理器...
  • zmbbb
  • zmbbb
  • 2016年08月21日 20:21
  • 226

S3C2440存储管理器详解

存储管理器概念,通俗而言就是可以通过它来访问外部存储设备的一种东西s3c2440地址空间分布 s3c2440对外引出了27根地址线ADDR0-ADDR26,访问空间为128MB,还对外引出了8根片选...
  • qq_31505483
  • qq_31505483
  • 2017年01月17日 19:28
  • 464

内存管理与垃圾回收2之内存溢出的三种情况及解决办法

相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识。 在解决j...
  • Kevin_jiang2011
  • Kevin_jiang2011
  • 2016年03月04日 09:25
  • 467

Android 简单的内存管理器

Android内存管理,实现的功能很简单,就是获取系统中正在运行的所有进程,并获取到每个进程所占的内存大小,以及系统剩余内存大小,并展示出来,然后通过点击每个进程可以选择是否要关闭进程(系统进程无法关...
  • super_spy
  • super_spy
  • 2014年04月11日 14:40
  • 1243

内存_新内存管理器

转自http://blog.csdn.net/kris_fei/article/details/8618587 ION概念:  ION是Google的下一代内存管理器,用来支持不同的内存分配机制,如C...
  • u011553162
  • u011553162
  • 2013年09月19日 17:49
  • 984

内存管理(二)借助工具解决内存问题

本文转载自http://www.csdn.net/article/1970-01-01/2826272 先下载转载文章中的实例MemoryProblems。1.悬挂指针问题悬挂指针(Dangling...
  • ideaspress
  • ideaspress
  • 2016年08月11日 17:29
  • 431

内存管理器

内存管理器                                                      一、手动计数器(MRC) 1.引用计数  1)每个对象有一个整数值,这个整数...
  • zouxianm
  • zouxianm
  • 2015年08月10日 22:46
  • 200

CPU与内存管理

大多数嵌入式处理器,如ARM  PPC等基本上都不提供I/O空间,而仅仅存在内存空间。内存空间可以通过地址和指针直接访问,程序和程序运行中使用的变量和其他数据都存在于内存空间中。 unsigned c...
  • smallfish_love
  • smallfish_love
  • 2016年02月29日 21:20
  • 264

EasyDarwin开源流媒体服务器内存管理优化

—本文由EasyDarwin开源团队成员Fantasy贡献前言最近在linux上跑EasyDarwin发现一个很奇怪的问题,当有RTSPSession连接上来的时候,发现进程的虚拟内存映射一下就多了6...
  • xiejiashu
  • xiejiashu
  • 2016年05月28日 11:24
  • 1149

【记录】存储管理器

1. 概要存储管理器负责管理2440的所有内存设备。2440的地址线ADDR0~ADDR26,即最大执行寻址2^27=128M空间。 CPU通过8根片选信号nGCS0~nGCS7,控制bank0~b...
  • iluzhiyong
  • iluzhiyong
  • 2016年08月08日 15:11
  • 460
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:内存管理器控制
举报原因:
原因补充:

(最多只允许输入30个字)