注册通行证 用户名 密码
  • 文章投稿
  • 博客
  • 论坛
  • 设为首页
  • 加入收藏
jztop.com网络技术
  • 首页
  • | iT新闻
  • | 操作系统
  • | 组网建网
  • | 网络安全
  • | 程序开发
  • | 办公一族
  • | 工具软件
  • | 网页制作
  • | 多媒体制作
  • | 网吧技术
  • | 服务器
  • | 专题教程
Vista | 软件评测 | 系统备份 | 优化 | 进程 | 聊天 | 病毒 | Linux | 黑客 | 防火墙 | 数据库 | Web开发 | Java | Word | 游戏 | 32位开发 | 移动开发
当前位置:首页 > 程序开发 > 32位开发 > C/C++ > 内容正文

浅谈C/C++内存泄漏及其检测工具

发布时间:2006-04-04 01:48:19 来源:温馨小屋 网友评论 0 条
  使用BoundsChecker检测内存泄漏:

  BoundsChecker采用一种被称为 Code Injection的技术,来截获对分配内存和释放内存的函数的调用。简单地说,当你的程序开始运行时,BoundsChecker的DLL被自动载入进程的地址空间(这可以通过system-level的Hook实现),然后它会修改进程中对内存分配和释放的函数调用,让这些调用首先转入它的代码,然后再执行原来的代码。BoundsChecker在做这些动作的时,无须修改被调试程序的源代码或工程配置文件,这使得使用它非常的简便、直接。

  这里我们以malloc函数为例,截获其他的函数方法与此类似。

  需要被截获的函数可能在DLL中,也可能在程序的代码里。比如,如果静态连结C-Runtime Library,那么malloc函数的代码会被连结到程序里。为了截获住对这类函数的调用,BoundsChecker会动态修改这些函数的指令。

  以下两段汇编代码,一段没有BoundsChecker介入,另一段则有BoundsChecker的介入:

126: _CRTIMP void * __cdecl malloc (
127: size_t nSize
128: )
129: {

00403C10 push ebp
00403C11 mov ebp,esp
130: return _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);
00403C13 push 0
00403C15 push 0
00403C17 push 1
00403C19 mov eax,[__newmode (0042376c)]
00403C1E push eax
00403C1F mov ecx,dword ptr [nSize]
00403C22 push ecx
00403C23 call _nh_malloc_dbg (00403c80)
00403C28 add esp,14h
131: }

  以下这一段代码有BoundsChecker介入:

126: _CRTIMP void * __cdecl malloc (
127: size_t nSize
128: )
129: {

00403C10 jmp 01F41EC8
00403C15 push 0
00403C17 push 1
00403C19 mov eax,[__newmode (0042376c)]
00403C1E push eax
00403C1F mov ecx,dword ptr [nSize]
00403C22 push ecx
00403C23 call _nh_malloc_dbg (00403c80)
00403C28 add esp,14h
131: }

  当BoundsChecker介入后,函数malloc的前三条汇编指令被替换成一条jmp指令,原来的三条指令被搬到地址01F41EC8处了。当程序进入malloc后先jmp到01F41EC8,执行原来的三条指令,然后就是BoundsChecker的天下了。大致上它会先记录函数的返回地址(函数的返回地址在stack上,所以很容易修改),然后把返回地址指向属于BoundsChecker的代码,接着跳到malloc函数原来的指令,也就是在00403c15的地方。当malloc函数结束的时候,由于返回地址被修改,它会返回到BoundsChecker的代码中,此时BoundsChecker会记录由malloc分配的内存的指针,然后再跳转到到原来的返回地址去。

  如果内存分配/释放函数在DLL中,BoundsChecker则采用另一种方法来截获对这些函数的调用。BoundsChecker通过修改程序的DLL Import Table让table中的函数地址指向自己的地址,以达到截获的目的。

  截获住这些分配和释放函数,BoundsChecker就能记录被分配的内存或资源的生命周期。接下来的问题是如何与源代码相关,也就是说当BoundsChecker检测到内存泄漏,它如何报告这块内存块是哪段代码分配的。答案是调试信息(Debug Information)。当我们编译一个Debug版的程序时,编译器会把源代码和二进制代码之间的对应关系记录下来,放到一个单独的文件里(.pdb)或者直接连结进目标程序,通过直接读取调试信息就能得到分配某块内存的源代码在哪个文件,哪一行上。使用Code Injection和Debug Information,使BoundsChecker不但能记录呼叫分配函数的源代码的位置,而且还能记录分配时的Call Stack,以及Call Stack上的函数的源代码位置。这在使用像MFC这样的类库时非常有用,以下我用一个例子来说明:


void ShowXItemMenu()
{
 …
 CMenu menu;

 menu.CreatePopupMenu();
 //add menu items.
 menu.TrackPropupMenu();
 …
}

void ShowYItemMenu( )
{
 …
 CMenu menu;
 menu.CreatePopupMenu();
 //add menu items.
 menu.TrackPropupMenu();
 menu.Detach();//this will cause HMENU leak
 …
}

BOOL CMenu::CreatePopupMenu()
{
 …
 hMenu = CreatePopupMenu();
 …
}

  当调用ShowYItemMenu()时,我们故意造成HMENU的泄漏。但是,对于BoundsChecker来说被泄漏的HMENU是在class CMenu::CreatePopupMenu()中分配的。假设的你的程序有许多地方使用了CMenu的CreatePopupMenu()函数,如CMenu::CreatePopupMenu()造成的,你依然无法确认问题的根结到底在哪里,在ShowXItemMenu()中还是在ShowYItemMenu()中,或者还有其它的地方也使用了CreatePopupMenu()?有了Call Stack的信息,问题就容易了。BoundsChecker会如下报告泄漏的HMENU的信息:

Function
File
Line

CMenu::CreatePopupMenu
E:/8168/vc98/mfc/mfc/include/afxwin1.inl
1009

ShowYItemMenu
E:/testmemleak/mytest.cpp
100

  这里省略了其他的函数调用

  如此,我们很容易找到发生问题的函数是ShowYItemMenu()。当使用MFC之类的类库编程时,大部分的API调用都被封装在类库的class里,有了Call Stack信息,我们就可以非常容易的追踪到真正发生泄漏的代码。

  记录Call Stack信息会使程序的运行变得非常慢,因此默认情况下BoundsChecker不会记录Call Stack信息。可以按照以下的步骤打开记录Call Stack信息的选项开关:

  1. 打开菜单:BoundsChecker|Setting…

  2. 在Error Detection页中,在Error Detection Scheme的List中选择Custom

  3. 在Category的Combox中选择 Pointer and leak error check

  4. 钩上Report Call Stack复选框

  5. 点击Ok

  基于Code Injection,BoundsChecker还提供了API Parameter的校验功能,memory over run等功能。这些功能对于程序的开发都非常有益。由于这些内容不属于本文的主题,所以不在此详述了。

  尽管BoundsChecker的功能如此强大,但是面对隐式内存泄漏仍然显得苍白无力。所以接下来我们看看如何用Performance Monitor检测内存泄漏。

  使用Performance Monitor检测内存泄漏

  NT的内核在设计过程中已经加入了系统监视功能,比如CPU的使用率,内存的使用情况,I/O操作的频繁度等都作为一个个Counter,应用程序可以通过读取这些Counter了解整个系统的或者某个进程的运行状况。Performance Monitor就是这样一个应用程序。

  为了检测内存泄漏,我们一般可以监视Process对象的Handle Count,Virutal Bytes 和Working Set三个Counter。Handle Count记录了进程当前打开的HANDLE的个数,监视这个Counter有助于我们发现程序是否有Handle泄漏;Virtual Bytes记录了该进程当前在虚地址空间上使用的虚拟内存的大小,NT的内存分配采用了两步走的方法,首先,在虚地址空间上保留一段空间,这时操作系统并没有分配物理内存,只是保留了一段地址。然后,再提交这段空间,这时操作系统才会分配物理内存。所以,Virtual Bytes一般总大于程序的Working Set。监视Virutal Bytes可以帮助我们发现一些系统底层的问题; Working Set记录了操作系统为进程已提交的内存的总量,这个值和程序申请的内存总量存在密切的关系,如果程序存在内存的泄漏这个值会持续增加,但是Virtual Bytes却是跳跃式增加的。

  监视这些Counter可以让我们了解进程使用内存的情况,如果发生了泄漏,即使是隐式内存泄漏,这些Counter的值也会持续增加。但是,我们知道有问题却不知道哪里有问题,所以一般使用Performance Monitor来验证是否有内存泄漏,而使用BoundsChecker来找到和解决。

  当Performance Monitor显示有内存泄漏,而BoundsChecker却无法检测到,这时有两种可能:第一种,发生了偶发性内存泄漏。这时你要确保使用Performance Monitor和使用BoundsChecker时,程序的运行环境和操作方法是一致的。第二种,发生了隐式的内存泄漏。这时你要重新审查程序的设计,然后仔细研究Performance Monitor记录的Counter的值的变化图,分析其中的变化和程序运行逻辑的关系,找到一些可能的原因。这是一个痛苦的过程,充满了假设、猜想、验证、失败,但这也是一个积累经验的绝好机会。

  总结

  内存泄漏是个大而复杂的问题,即使是Java和.Net这样有Gabarge Collection机制的环境,也存在着泄漏的可能,比如隐式内存泄漏。由于篇幅和能力的限制,本文只能对这个主题做一个粗浅的研究。其他的问题,比如多模块下的泄漏检测,如何在程序运行时对内存使用情况进行分析等等,都是可以深入研究的题目。如果您有什么想法,建议或发现了某些错误,欢迎和我交流。
上一页 1 2
关于 浅谈C 浅谈C /C++ 内存泄漏 检测工具 的新闻
  • 内存泄漏,走开 轻松搞定Java内存泄漏
  • 菜鸟也能搞定C++内存泄漏
  • Java理论和实践:用软引用阻止内存泄漏
  • C/C++作用域引申出的编码规范
  • 浅谈CLR的内存分配和回收机制
【评论】【收藏本文】【打印】【关闭】
上一篇文章:菜鸟也能搞定C++内存泄漏
下一篇文章:内存陷阱 驯服C++中的野指针
讨论区
查看
已有 0 位对此新闻感兴趣的网友发表了看法
匿名发表
注册通行证 登陆
图文阅读推荐
全站资源
  • 微软官方入门教程19:轻松掌握Vista系统的快
  • 微软2008大冲击,预借Vista SP1力促Vista市
  • 在收件箱中获得 Windows Vista 的最新更新
  • 微软官方Vista入门教程全集19篇(Vista学院
  • Windows Vista 的成功将势不可挡
  • 快快抛弃Vista,拥抱XP SP3!你觉得呢?
  • 浅谈Vista系统关闭虚拟内存与使用内存盘加速
  • 嘿嘿,按下键盘上面的三个键,马上让你的Vi
  • Windows Vista的盗版率只有Windows XP的一半
  • 3DMark和PCMark Vantage新版将只支持Vista系
阅读排行
  • Borland 发布C++ Builder 2006 RAD 环境
  • C/C++程序员应聘常见面试题深入剖析
  • Visual C++常用数据类型转换详解
  • C++中的 static 关键字
  • 利用VC++实现局域网实时视频传输
  • 浅谈C/C++内存泄漏及其检测工具
  • 英国投票否决C++/CLI 微软强攻ISO标准受挫
  • VC++下用MSComm控件实现串口通讯
  • 伪随机数生成及在VC++中的实现
  • VC++编程实现对波形数据的频谱分析
最新技术文档
  • C++中用赋值形式op=取代单独形式op
  • C++启蒙之用C++制作程序
  • C++启蒙之C++中的类
  • 解析C++/CLI之头文件、内联函数与数组
  • C++启蒙之控制结构
  • C++启蒙之注释符号
  • 了解C++异常处理的系统开支
  • C++语言学习之从结构到类
  • C++中理解“传递参数”和异常之间的差异
  • C++中禁止异常信息传递到析构函数外
专题教程
  • 大话G游 专题:手机病毒揭密
  • ARP攻击防范与解决方案 路由故障处理手册
  • Picasa中文版_Picasa教程 专题:清除流氓软件
  • Firefox专题 seo搜索引擎优化专区
  • 重装Windows必知的事情 装机之必备软件大行动
病毒专杀栏
  • 杀毒软件反被病毒杀 连"救命"都不能喊
  • 金山ARP防火墙
  • 还原卡神话破灭“机器狗”病毒来势汹汹
  • cctv经济半小时:你的手机现在安全吗?
  • 新挂马方式开始流行 ARP挂马称雄局域网
  • 木马和病毒清除的通用解法
  • IP地址不再冲突 查找ARP攻击者元凶
  • 教你几招识别和防御Web网页木马
  • 分析:封杀BT只是暂时的止痛药
  • QQ爆危险漏洞,“QQ游戏邀请大盗”邀请你玩病
关于我们 | 诚聘英才 | 联系我们 | 版权声明 | 网站大事 | 网站地图 | 意见建议
CopyRight 2005-2007 Jztop.Com 版权所有 未经许可 请勿转载