引言

对于C++的开发者来说,内存泄露是一类耗时的bug。检查内存泄露总是让人很抓狂,如果出问题的代码不是你写的,或者代码量很大的时候,事情就更糟糕了。

现在市场上有很多工具可以用于检查分析内存泄露的问题,但是一般都不是免费的。Windbg是一款功能强大又可免费使用的内存泄露检查工具,通过Windbg可以初步定位怀疑有内存泄露的代码位置。COM接口相关的问题在本文档中不涉及。

Windbg是微软提供的一款功能强大的用户内核空间调试器,可以从以下地址下载

http://msdn.microsoft.com/en-us/library/windows/hardware/ff551063(v=vs.85).aspx

使用Windbg

使用Windbg的准备:

  1. 在symbol file中配置Microsoft Symbol Servers的目录

    在Windbg的File==>Symbol File Path中输入:

    SRV*d:\symbols*http://msdl.microsoft.com/download/symbols

    d:\symbols表示从Microsoft Symbol Servers下载的符号文件存放目录,若从未下载过系    统的符号文件,Windbg会自动下载。

  2. 在Symbol File Path中输入自己程序的PDB文件目录(包括DLL的PDB),可添加    多个,以分号隔开如:

    D:\myApp\pdb;SRV*d:\symbols*http://msdl.microsoft.com/download/symbols

    自己程序的PDB目录放在前面,Windbg查找较快。

  3. 配置操作系统标记,开启可能有内存泄露程序的用户堆栈跟踪。利用gflags工具很    容易实现(gflags同样也是微软Windbg工具之一),安装Windbg时会同时安装该工    具。使用如下的命令行实现

    gflags.exe /i MemoryLeak.exe +ust

    MemoryLeak.exe是怀疑有内存泄露的程序,只需要exe名称,不用路径。

  4. 配置Source File Path,输入相关的程序代码目录,多个目录用分号隔开
  5. 配置Image File Path,输入相关程序的2进制文件目录,包括exe\dll,多个目录用分    号隔开

    以下利用Test2.exe作为测试程序,相关代码如下:

    int _tmain(int argc, _TCHAR* argv[])

    { while(1)

    {

    AllocateMemory();

    }

    return 0;

    }

    void AllocateMemory()

    {

    int* a = new int[2000];

    ZeroMemory(a, 8000);

    Sleep(1);

    }

    下图是运行gflags的截图

    配置好Windbg后,运行存在内存泄露的程序,在Windbg中加载该程序,File==>Attach to a Process,或者使用快捷键F6,如下图

    加载Test2.exe后,Test2.exe会暂停运行,在Windbg中输入命令(有关heap的命令参考Windbg的帮助文档,有详细介绍)

    !heap -s

    会得到类似如下的输出内容

    0:001> !heap -s

    NtGlobalFlag enables following debugging aids for new heaps:

    validate parameters

    stack back traces

    Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast

    (k) (k) (k) (k) length blocks cont. heap

    -----------------------------------------------------------------------------

    00150000 58000062 1024 12 12 1 1 1 0 0 L

    00250000 58001062 64 24 24 15 1 1 0 0 L

    00260000 58008060 64 12 12 10 1 1 0 0

    00330000 58001062 64576 47404 47404 13 4 1 0 0

    -----------------------------------------------------------------------------

    继续运行Test2.exe一段时间后,在Windbg中暂停,再次运行命令

    !heap -s

    得到输出

    0:001> !heap -s

    NtGlobalFlag enables following debugging aids for new heaps:

    validate parameters

    stack back traces

    Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast

    (k) (k) (k) (k) length blocks cont. heap

    -----------------------------------------------------------------------------

    00150000 58000062 1024 12 12 1 1 1 0 0 L

    00250000 58001062 64 24 24 15 1 1 0 0 L

    00260000 58008060 64 12 12 10 1 1 0 0

    00330000 58001062 261184 239484 239484 14 4 1 0 0

    -----------------------------------------------------------------------------

    加粗字段部分显示了有增长的堆信息,上面的输出显示堆00330000 有增长。

    执行命令

    !heap -stat –h 00330000

    该命令会显示有增长堆的统计信息,输出如下:

    0:001> !heap -stat -h 00330000

    heap @ 00330000

    group-by: TOTSIZE max-display: 20

    size #blocks total ( %) (percent of total busy bytes)

    1f64 76c6 - e905f58 (99.99)

    1800 1 - 1800 (0.00)

    824 2 - 1048 (0.00)

    238 2 - 470 (0.00)

    244 1 - 244 (0.00)

    4c 5 - 17c (0.00)

    b0 2 - 160 (0.00)

    86 2 - 10c (0.00)

    50 3 - f0 (0.00)

    74 2 - e8 (0.00)

    38 4 - e0 (0.00)

    48 3 - d8 (0.00)

    c4 1 - c4 (0.00)

    62 2 - c4 (0.00)

    be 1 - be (0.00)

    b8 1 - b8 (0.00)

    ae 1 - ae (0.00)

    ac 1 - ac (0.00)

    55 2 - aa (0.00)

    a4 1 - a4 (0.00)

    加粗部分显示有0x76c6个1f64 (8036即代码中2000*4)大小的内存块被分配,如此大量同样大小的内存块分配,可以猜测这就可能是内存泄露的地方。

    下一步是获取到这些被分配内存的地址,使用命令

    !heap -flt s 1f64

    该命令掉其它的内存块,只显示大小为1f64 的内存块信息

    输出结果如下:

    0:001> !heap -flt s 1f64

    _HEAP @ 150000

    _HEAP @ 250000

    _HEAP @ 260000

    _HEAP @ 330000

    HEAP_ENTRY Size Prev Flags UserPtr UserSize - state

    003360e0 03f0 0000 [07] 003360e8 01f64 - (busy)

    00338060 03f0 03f0 [07] 00338068 01f64 - (busy)

    00339fe0 03f0 03f0 [07] 00339fe8 01f64 - (busy)

    0033bf60 03f0 03f0 [07] 0033bf68 01f64 - (busy)

    0033dee0 03f0 03f0 [07] 0033dee8 01f64 - (busy)

    01420040 03f0 03f0 [07] 01420048 01f64 - (busy)

    01421fc0 03f0 03f0 [07] 01421fc8 01f64 - (busy)

    01423f40 03f0 03f0 [07] 01423f48 01f64 - (busy)

    01425ec0 03f0 03f0 [07] 01425ec8 01f64 - (busy)

    01427e40 03f0 03f0 [07] 01427e48 01f64 - (busy)

    01429dc0 03f0 03f0 [07] 01429dc8 01f64 - (busy)

    0142bd40 03f0 03f0 [07] 0142bd48 01f64 - (busy)

    0142dcc0 03f0 03f0 [07] 0142dcc8 01f64 - (busy)

    0142fc40 03f0 03f0 [07] 0142fc48 01f64 - (busy)

    01431bc0 03f0 03f0 [07] 01431bc8 01f64 - (busy)

    01433b40 03f0 03f0 [07] 01433b48 01f64 - (busy)

    01435ac0 03f0 03f0 [07] 01435ac8 01f64 - (busy)

    01437a40 03f0 03f0 [07] 01437a48 01f64 - (busy)

    014399c0 03f0 03f0 [07] 014399c8 01f64 - (busy)

    0143b940 03f0 03f0 [07] 0143b948 01f64 - (busy)

    0143d8c0 03f0 03f0 [07] 0143d8c8 01f64 - (busy)

    0143f840 03f0 03f0 [07] 0143f848 01f64 - (busy)

    014417c0 03f0 03f0 [07] 014417c8 01f64 - (busy)

    01443740 03f0 03f0 [07] 01443748 01f64 - (busy)

    014456c0 03f0 03f0 [07] 014456c8 01f64 - (busy)

    01447640 03f0 03f0 [07] 01447648 01f64 - (busy)

    014495c0 03f0 03f0 [07] 014495c8 01f64 - (busy)

    0144b540 03f0 03f0 [07] 0144b548 01f64 - (busy)

    0144d4c0 03f0 03f0 [07] 0144d4c8 01f64 - (busy)

    0144f440 03f0 03f0 [07] 0144f448 01f64 - (busy)

    014513c0 03f0 03f0 [07] 014513c8 01f64 - (busy)

    01453340 03f0 03f0 [07] 01453348 01f64 - (busy)

    014552c0 03f0 03f0 [07] 014552c8 01f64 - (busy)

    01457240 03f0 03f0 [07] 01457248 01f64 - (busy)

    014591c0 03f0 03f0 [07] 014591c8 01f64 - (busy)

    0145b140 03f0 03f0 [07] 0145b148 01f64 - (busy)

    0145d0c0 03f0 03f0 [07] 0145d0c8 01f64 - (busy)

    0145f040 03f0 03f0 [07] 0145f048 01f64 - (busy)

    01460fc0 03f0 03f0 [07] 01460fc8 01f64 - (busy)

    01462f40 03f0 03f0 [07] 01462f48 01f64 - (busy)

    01464ec0 03f0 03f0 [07] 01464ec8 01f64 - (busy)

    01466e40 03f0 03f0 [07] 01466e48 01f64 - (busy)

    01468dc0 03f0 03f0 [07] 01468dc8 01f64 - (busy)

    任意找到一行UsrPtr 对应列的值,使用如下命令显示该UsrPtr 对应的调用堆栈

    !heap -p -a UsrPtr

    选择上述输出中加粗的一行,执行命令!heap -p -a 0143d8c8后可以得到如下的输出

    0:001> !heap -p -a 0143d8c8

    address 0143d8c8 found in

    _HEAP @ 330000

    HEAP_ENTRY Size Prev Flags UserPtr UserSize - state

    0143d8c0 03f0 0000 [07] 0143d8c8 01f64 - (busy)

    Trace: 0025

    7c96d6dc ntdll!RtlDebugAllocateHeap+0x000000e1

    7c949d18 ntdll!RtlAllocateHeapSlowly+0x00000044

    7c91b298 ntdll!RtlAllocateHeap+0x00000e64

    102c103e MSVCR90D!_heap_alloc_base+0x0000005e

    102cfd76 MSVCR90D!_heap_alloc_dbg_impl+0x000001f6

    102cfb2f MSVCR90D!_nh_malloc_dbg_impl+0x0000001f

    102cfadc MSVCR90D!_nh_malloc_dbg+0x0000002c

    102db25b MSVCR90D!malloc+0x0000001b

    102bd691 MSVCR90D!operator new+0x00000011

    102bd71f MSVCR90D!operator new[]+0x0000000f

    4113d8 Test2!AllocateMemory+0x00000028

    41145c Test2!wmain+0x0000002c

    411a08 Test2!__tmainCRTStartup+0x000001a8

    41184f Test2!wmainCRTStartup+0x0000000f

    7c816fd7 kernel32!BaseProcessStart+0x00000023

    加粗字体部分即为代码中出现内存泄露的位置。

    PS:有时!heap -s命令并不能找出有明显增长的堆。这时可以先用 !heap -stat -h 命令将所有堆的block 和 size列出来,找到可能有增长的堆,再用命令 !heap -flt s SIZE (SIZE即为怀疑的堆大小)

WinDebug查找内存泄露相关推荐

  1. 使用BoundsChecker查找内存泄露

    以前搞开发时,曾经写过类似的文章,不过带不出来,最近VS推出了新版VS2010,而BoundsChecker也推出了适用于vc6, vs2005,vs2008,vs2010的新版本,于是下载下来试了一 ...

  2. windbg小工具umdh查找内存泄露

    UMDH 是windows debug tools 下的一款命令行工具,它的全名是User-Mode Dump Heap 这个工具会分析当前进程在堆上分配的内存,并有两种模式 1. 进程分析模式,这个 ...

  3. Android App定位和规避内存泄露方法研究

    from:http://site.douban.com/android/widget/notes/350758/note/167481484/ 工作中刚好用到,网上搜到的,觉得不错,与大家分享 And ...

  4. Visual Leak Detector 帮助检查内存泄露

    计算机为 win7,X64,vs2010 http://vld.codeplex.com/releases 从上述地址下载vld-2.3-setup.exe 安装后,打开vs2010在项目属性--VC ...

  5. VC内存泄露检查工具:Visual Leak Detector

    www.diybl.com 时间:2009-04-12 作者:匿名 编辑:sky 初识Visual Leak Detector        灵活自由是C/C++语言的一大特色,而这也为C/C++程序 ...

  6. VC内存泄露检查工具:VisualLeakDetector

    From: http://www.xdowns.com/article/170/Article_3060.html 初识Visual Leak Detector        灵活自由是C/C++语言 ...

  7. Egret在Chrome浏览器中的内存占用(内存泄露)

    参考: 怎样查看Chrome的内存占用情况 JS内存泄漏排查方法(Chrome Profiles) chrome内存泄露(一).内存泄漏分析工具 chrome内存泄露(二).内存泄漏实例 目录: 一 ...

  8. Android进程与内存及内存泄露

    本人博客原文 第一部分Android进程 1.1.Android进程简介 每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,而每一个虚拟机实例都是一个独立的进程空间,不同的应用在 ...

  9. openssl内存分配,查看内存泄露

    openssl内存分配  用户在使用内存时,容易犯的错误就是内存泄露.当用户调用内存分配和释放函数时,查找内存泄露比较麻烦.openssl提供了内置的内存分配/释放函数.如果用户完全调用openssl ...

最新文章

  1. Java比较数量怎么比较_java - 如何在Java数量比较字符 - SO中文参考 - www.soinside.com...
  2. 二叉树的先序遍历(非递归)
  3. 投资房地产,甘当'接盘侠'才能赚到钱?
  4. javaweb学习总结(二十四)——jsp传统标签开发
  5. 为Liferay Server分配Perm,Heap Size
  6. 香农定理和奈奎斯特定理区别_「中考复习」三大变换之旋转(旋转的构造-托勒密定理)...
  7. python面向对象继承_四. python面向对象(继承)
  8. php urledcode_用JavaScript实现PHP的urldecode函数
  9. javascrip部分
  10. Spring Cloud(Greenwich版)-02-服务注册与服务发现-Eureka入门
  11. Excel做数据分析?是真的很强
  12. python fields_Python fields.Nested方法代码示例
  13. 算法(一) 两圆公切线
  14. python爬取豆瓣电影信息_Python爬虫入门 | 爬取豆瓣电影信息
  15. python tensorflow教程_真正从零开始,TensorFlow详细安装入门图文教程!
  16. Tensorflow API 讲解——tf.estimator.Estimator
  17. RN 与 Android 代码交互
  18. 第三方登录之QQ登录集成(二)
  19. 上“天”入“地”,都少不了亚马逊云科技这个云引擎
  20. 实现搜索历史的记录 (记录到本地存储中)

热门文章

  1. 鲁棒性Robustness
  2. 计算机管理文件和文件夹的实验结果及分析,大学计算机实验报告1
  3. 主流电脑配置的通用引导文件,包含CLOVER与OpenCorer双引导
  4. 软件需求规格说明书——学生成绩查询系统
  5. 使用Opencv打开手机摄像头
  6. ubuntu免费下载----百度网盘
  7. 三自由度机械臂连续轨迹控制Simulink仿真
  8. 【梅哥的Ring0湿润插入教程】【番外篇四】抓取盛大GPK驱动保护文件及简略逆向...
  9. mysql某字段值转成大写_获取字段值,并使用MySQL将其转换为大写
  10. 资产管理系统是管钱的吗?不完全对