Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1203639
  • 博文数量: 181
  • 博客积分: 6155
  • 博客等级: 准将
  • 技术积分: 1805
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-05 09:24
文章分类

全部博文(181)

文章存档

2015年(2)

2014年(3)

2013年(9)

2012年(29)

2011年(30)

2010年(36)

2009年(40)

2008年(32)

分类: C/C++

2012-07-27 16:58:25

这两天调一个程序,发现每次退出都有内存泄漏,在此总结一个调试内存泄漏的好方法。

    对于比较明显的内存泄漏,vc2008是比较容易定位的,在Debug模式F5运行,退出时会列出引起内存泄漏的问题代码所在文件及行号;但如果是比较隐蔽的内存泄漏,Output输出窗口只会输出部分信息,形式如下:

    Detected memory leaks!
    Dumping objects ->
    {858} normal block at 0x003ED290, 12 bytes long.
    Data: < H         > A0 5C 48 00 00 00 00 00 00 00 00 00
    Object dump complete.

    我们来看一下输出信息的格式。

    {xxx} — 表示程序运行后第xxx次内存分配发生了泄漏,即内存分配编号;

    xxx block — 表示内存块类型,包括三种:普通(normal)、客户端(client)和运行时(CRT);

    at 0xFFFFFFFF — 表示发生泄漏的内存地址,用十六进制表示;

    xx bytes long — 表示发生泄漏的内存大小;

    Data:xxx — 表示内存数据信息,一般输出前16字节的内容。

    内存泄漏的地址、大小和数据信息很难对我们的定位起到有效地帮助,但是如果我们能够让程序停在分配有泄漏的内存的地方,然后根据调用堆栈(Call Stack)里的信息,就可以很方便的定位是哪个语句出现问题。幸运的是,VC的C运行库(C run-time library)为我们提供了一个这样的函数:CrtSetBreakAlloc,声明如下: 

long _CrtSetBreakAlloc( long lBreakAlloc );

    其中参数lBreakAlloc即为内存分配号(花括号中的数字)。我们只要在程序初始化函数中添加语句:_CrtSetBreakAlloc(858)就可以使程序在Debug F5运行时自动断点到我们想要的位置。

    使用这个函数进行内存泄漏定位有个条件,即发生内存泄漏的分配号是固定的,每次启动程序不会随机变化 

    但是如果我们的程序比较大,内存泄漏也不止一个,用上面的方法调试可能会很麻烦,需要经常更新代码并重新编译程序。还有更好的方法。

 

    首先确定自己的程序使用的是哪个C运行库,方法是程序Propertise -> C/C++ -> Code Generation -> Runtime Library选项,若为Multi-threaded Debug(/MTd),则为静态链接,若为Multi-threaded Debug DLL(/MDd),则为动态链接。

    然后按F11启动程序,程序会停在入口点。此时调出Watch窗口,

    静态链接 在name项中输入_crtBreakAlloc,在value项中输入你要定位的内存分配编号;

    动态链接 在name项中输入{,,msvcr90d.dll}_crtBreakAlloc,在value项中输入内存分配号。注意msvcr90d.dll是vc2008环境使用的运行库dll,若你用的是其他版本vc,请换成对应版本的dll。

    现在按F5运行,程序就会自动定位到你要调试的内存分配语句,这时候看call stack的信息会很有帮助。

    这个办法使用起来非常灵活,只要你确定内存分配号是不变的,就可以方便地为程序设置断点了,省去了更新代码和编译的麻烦。这个方法几乎是当前进行内存泄漏调试的最有效方法,如果你发现内存泄漏的分配号是变化的,可以想办法去掉一些没有问题的内存申请(多半是界面和多线程涉及到的申请),尽量使有问题的分配固定,就可以用这个方法排错了。

阅读(4050) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~