文章目录


 

1、使用windbg中的umdh检查

1.1、 工具下载

下载地址:http://www.technlg.net/windows/windgb-download/

注意:最好下载x64位的安装包,因为32位的安装包可能会安装不成功。

1.2、环境配置

计算机-》右击-》属性-》高级系统设置-》环境-》系统变量-》Path-》编辑-》变量值 处后加入:

c:\Program Files\Debugging Tools for Windows(x64)


  • 1

注意:此路劲请参考个人的安装路径。

1.3、利用工具umdh(user-mode dump heap)分析

(1)由于在安装路径下有 gflags.exe、umdh.exe 这两个可执行文件,且在步骤2中已加入环境变量,下一步可直接在dos界面输入命令;

(2)桌面左下角-》开始 处输入cmd命令,打开dos界面窗口后,输入命令:gflags.exe,会出现下图,并按照下图进行相关设置:

【内存检测系列】window下windbg内存泄漏检查_#include

【内存检测系列】window下windbg内存泄漏检查_#define_02

(3)gflags标志设置好后,开启cmd

【内存检测系列】window下windbg内存泄漏检查_3c_03

键入要定位内存泄露的程序gflags.exe /I memroyleak.exe(程序名称)+ust

如图成功后,开启memoryleak.exe程序,即运行需要测试的程序。

1.4、利用umdh创建heap快照

【内存检测系列】window下windbg内存泄漏检查_内存泄漏_04

命令格式:umdh –pn:memoryleak.exe(程序名称) –f:snap1.log(日志名称)

程序运行一段时间后或者程序占用内存增加时,将memoryleak.exe退出。

然后再次创建heap快照,命令行无差别,snap1.log改为snap2.log或者其他。

设置好程序的符号路径,如下图:

【内存检测系列】window下windbg内存泄漏检查_3c_05

设置好后可以开始分析heap前后两个快照的差异

【内存检测系列】window下windbg内存泄漏检查_句柄_06

分析差异命令:umdh –d snap1.log snap2.log –f:result.txt

【内存检测系列】window下windbg内存泄漏检查_内存泄漏_07

分析完成后查看结果result.txt,可以指定具体的输出文件夹,不指定的话,输出在桌面的个人文件夹《以自己名字命名的文件夹》

红色为umdh定位出来的泄露点,我们在查看源代码

【内存检测系列】window下windbg内存泄漏检查_内存泄漏_08

这样我们就可以修改代码中内存泄露的地方了。

注意:利用Umdh创建Heap快照 步骤设置程序的符号路径,此符号路径是一个在线网址,因此需要电脑联网;对于我们来说只能通过下载符号离线包的方式,离线包的路径为:

https://developer.microsoft.com/en-us/windows/hardware/download-symbols

关键词:Download Windows Symbol Packages

但是不下载此包,符号路径会自动设置为 %windir%\symbols 即windows自带的符号路径,目前未发现问题

参考网址:http://blog.csdn.net/chenyujing1234/article/details/11918987

2、使用windbg中的htrace检查

2.1、准备工作

1 打开windbg

2 File->Symbol File Path,配置好符号文件(PDB)

【内存检测系列】window下windbg内存泄漏检查_#define_09

3 File->Attache to a Process->可以选择一个运行中的进程,并对其进行调试。

【内存检测系列】window下windbg内存泄漏检查_#include_10

2.2、快照

1 让程序运行(按F5或输入命令:g)

2 程序执行到想要快照的时候,中断程序(Debug->Break)

3 开启句柄栈回溯(输入命令:!htrace -enable)

4 抓取快照(输入命令:!htrace -snapshot)

2.3 、执行操作,比较差异

1 让程序运行(按F5或输入命令:g)

2 执行操作

3 中断程序(Debug->Break)

4 !htrace -diff

2.4、找到代码行

1 查看泄漏的句柄

从下图中可以看出有一个句柄泄漏,该句柄时通过CreateEvent创建出来的事件,通过栈回溯可以看出该事件创建事件的函数:vlc_tester::on_pB_Open_clicked。

【内存检测系列】window下windbg内存泄漏检查_句柄_11

2 查找具体的代码行

lsa vlc_tester!vlc_tester::on_pB_Open_clicked+0x000000000000002c

从下图中可以看出具体的代码。

【内存检测系列】window下windbg内存泄漏检查_句柄_12

3 如果在栈回溯中看不到有用信息,还可以考虑在代码中打印出线程ID,再与句柄泄漏所在线程对比,确认在哪个线程泄漏的,缩小范围。

4 根据栈回溯的模块或模块地址也能找到是哪个模块泄漏的句柄。

2.5、Process Explorer

Process Explorer

1 显示句柄详细信息

【内存检测系列】window下windbg内存泄漏检查_#define_13

2 查看程序占用的句柄,如果是命名句柄,可以可以直接在代码中找到该句柄

【内存检测系列】window下windbg内存泄漏检查_3c_14

参考网址:https://www.jianshu.com/p/3c88d5adc7bc

3、windows下内存泄漏–CRTDBG

3.1、检测是否存在内存泄漏

Windows平台下面Visual Studio 调试器和C运行时(CRT)库为我们提供了检测和识别内存泄漏的有效方法,原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏。在vs中启用内存检测的方法如下:

(1)、程序文件开头处包含以下语句,不要更改顺序

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>


  通过包括crtdbg.h,将malloc和free函数映射到它们的调试版本,即_malloc_dbg和 _free_dbg,这两个函数将跟踪内存分配和释放。 此映射在调试版本(在其中定义了_DEBUG)中发生。 发布版本使用普通的 malloc 和 free 函数。

  #define 语句将 CRT 堆函数的基版本映射到对应的“Debug”版本。 并非绝对需要该语句;但如果没有该语句,内存泄漏转储包含的有用信息将较少。

(2)、在添加了上述语句之后,可以通过在程序中包括以下语句(通常应恰好放在程序退出位置之前)来转储内存泄漏信息:

_CrtDumpMemoryLeaks();


  • 1

示例代码:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

int main(int argc,char** argv)
{
char *str = (char*)malloc(20 * sizeof(char));
_CrtDumpMemoryLeaks();
return 0;
}


 

 当在调试器下运行程序时(按F5),_CrtDumpMemoryLeaks将在VS最下栏 “输出”窗口 -》显示输出来源-》调试 中显示内存泄漏信息。如果没有使用#Define _CRTDBG_MAP_ALLOC语句,输出窗口则不显示内存泄漏的具体位置。

注意:

  如果程序总是在同一位置退出,调用 _CrtDumpMemoryLeaks 将非常容易。如果程序从多个位置退出,则无需在每个可能退出的位置放置对 _CrtDumpMemoryLeaks 的调用,而可以在程序开始处包含以下调用:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

 该语句在程序退出时自动调用 _CrtDumpMemoryLeaks。必须同时设置 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF两个位域。

示例代码:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

int main(int argc,char** argv)
{
char *str = (char*)malloc(20 * sizeof(char));
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
return 0;
}


 

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>


void check_mem()
{
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
}

int main(int argc,char** argv)
{
char *str = (char*)malloc(20 * sizeof(char));
atexit(check_mem);
return 0;
}


注:上述方法在测试调用的第三方库是否存在内存泄漏时,并不能显示泄露函数在哪个文件、哪一行,只会简单的显示,如:

Detected memory leaks!  
Dumping objects ->
{223} normal block at 0x003CF650, 4 bytes long.
Data: < < > E8 F6 3C 00


 

此时,需要在程序的入口处加入:

_CrtSetBreakAlloc(223);


在vs中按F5调试,在堆栈信息中就可以看到泄露的具体位置。其中223表示{223} normal block at 0x003CF650, 4 bytes long.