VLD(Visual LeakDetector)内存泄露库的使用

VLD简介

    因为C/C++语言没有所谓的垃圾收集器,内存的分配和释放都须要程序员本身来控制,这会给C/C++程序员带来必定的困难。当您的程序愈来愈复杂时,它的内存管理也会变得愈来愈困难。内存泄漏、内存越界是最多见的内存问题之一。
    内存泄漏若是不是很严重的话,在短期内对程序不会形成太大的影响,并且在进程终止的时候,全部分配的内存都会释放掉。可是对于长时间运行的程序,其破坏力是惊人的,从性能降低到内存耗尽,甚至会影响到其它程序的正常运行。
    此外,内存问题存在一个共同的特色,它自己并不会有很明显的现象,当有异常出现时就很难检查问题的缘由所在,这给调试内存问题带来了很大的难度。
VLD是一款用于VisualC++的免费内存泄漏检查工具。能够在codeproject.com网站上找到,相比其它的内存泄漏哦给你根据,他在检查内存泄漏的同事,还具备以下特色:
    1)  能够获得内存泄漏点的调用堆栈,若是能够的话,还能够获得其所在的文件及行号;
    2)  能够获得泄漏内存的完整数据;
    3)  恶意设置内存泄漏报告的级别;
    4)  它以动态库的形式提供,无需编译源代码,只须要很小的改动程序;
    5)  源代码使用GNU许可发布,并有详细的文档及其注释。
    从使用的角度讲,VLD简单易用,对于使用者本身的代码中惟一须要修改的地方是#include VLD的头文件后正常运行本身的程序就能够发现内存问题。从研究角度上讲,若是输入到VLD源代码,能够学习到堆内存分片与释放的原理、内存检查的原理机器内存操做的经常使用技巧等。

VLD使用

    VLD网址:http://vld.codeplex.com/
    http://www.codeproject.com/Articles/9815/Visual-Leak-Detector-Enhanced-Memory-Leak-Detectio
    下载Visual LeakDetector,当前版本2.2.3,打开Visual C++ IDE的"工具"→"选项"→"项目和解决方案"→"VC++ 目录",在"包含文件"中增长VLD的头文件路径"\include"路径,在"库文件"增长VLD库文件的"\lib\Win32"路径,此外动态库的"\bin\Win32"路径在安装时已经添加到环境变量里面了,如果未添加,则须要手动拷贝"\bin\Win32"下的文件到可执行文件所在的目录中(拷贝的文件有dbghelp.dll/Microsoft.DTfW.DHL.manifest/vld_x86.dll/vld.ini)。
接下来须要将VLD加入到本身的代码中。方法很简单,只要在包含入口函数的.cpp文件中包含vld.h就能够。若是这个cpp文件中包含了stdafx.h,则将包含vld.h的语句放在stdafx.h的包含语句以后,不然放在最前面。
    示例程序:
#include<vld.h>                 // 包含VLD的头文件
#include<stdlib.h>
#include<stdio.h>
void f()
{
    int *p = new int(0x12345678);
    printf("p=%08x, ", p);
}
int main()
{
    f();
    return 0;
}
    注:VLD只能在Windows下使用,在包含vld.h头文件时增长预编译选项。
    注:在Release模式下,不会连接VisualLeak Detector。
    注:Visual LeakDetector有一些配置项,能够设置内存泄露报告的保存地(文件、调试器),拷贝"\Visual Leak Detector"路径下的vld.ini文件到执行文件所在的目录下(在IDE运行的话,则须要拷贝到工程目录下),修改如下项:
ReportFile =.\memory_leak_report.txt 
ReportTo = both

VLD工具原理

    下面咱们来看看VLD是如何工做的。在VisualC++中内置工具CRT Debug Heap工具,在使用Debug版本分配内存时,它会在内存块中记录分配该内存的文件名和行号。当程序退出时CRT会在main函数返回时作一些清理工做,此时检查调试堆内存,若是仍然有内存没释放,则必定存在内存泄漏问题。从这些没有被释放的内存块的头中能够获得文件名和行号。这种静态的方法能够检查出内存泄漏,可是不知道泄漏到底是怎么发生的,也不知道该内存分配语句是如何被执行到的,想要了解这些必须对内存分配过程进行动态跟踪。VLD就是这样作的,在每次内存分配的时候记录其上下文,当程序退出时对检测到的内存泄漏查找其上下文信息,并转换成报告输出到Output中。

初始化

    VLD要记录每次的内存分配,它经过Windows提供的分配钩子allocation hooks来监视调试堆内存的分配。它是一个用户自定义的回调函数,在每次从堆中分配内存以前被调用,在初始化是VLD使用_CrtSetAllocation注册这个钩子函数。
全局变量在程序初始化时就初始化,若是将VLD做为一个全局变量就能够与程序一块儿启动,可是C/C++并无约定全局变量初始化的顺序,若是其它全局变量的构造函数中有内存分配则可能没法检测到。所以,VLD使用C/C++提供的#pragma init_seg来减小其它全局变量在它以前进行初始化。根据#pragma init_seg的定义,全局变量初始化分为3个阶段,首先是compiler阶段,通常进行C语言运行时库的初始化;而后是lib段,通常用于第三方类库的初始化扽;最后是user段,大部分的初始化都在这个阶段进行。

记录内存分配

    一个内存分配钩子函数须要具备以下的定义:
int AllocHook(int allocType, void*userData, size_t size,int blockType, long requestNumber, onst unsigned char*filename, int lineNumber);
    该函数须要在VLD初始化时被注册,每次从堆中分配内存前被调用,它须要处理的事情就是记录下此时的调用堆栈和此时堆内存分配的惟一标识requestNumber。
获得当前堆栈的二进制表示并非很复杂的事情,可是由于不一样的体系结构、不一样的编译器、不一样的操做系统所产生的堆栈内容是不同的,要解释堆栈并获得整个函数的调用过程比较复杂。不过Windows提供了一个StackWalk64函数能够得到堆栈的内容。
    VLD是经常使用的C/C++内存泄漏检查工具,能够在ViusalC++中使用,在Viusal Studio 2008和2010中使用须要注意两点:
    1)  版本问题:VLD已经更新到2.2版本,修正了许多bug,并且在2010版本下工做良好,VisualC++ 6.0推荐使用1.0版本,1.9b版本不是很稳定不建议使用,2.2版本的下载网址为http://vld.codeplex.com.
    2)  设置变化:VC++Directories设置已经变化位置,在2010中设置过程以下:
View | Other Window | Property Manager
Go to "VC++ Directories" settings
Set include folder path
Set lib folder path
    点OK,咱们就设置好了include和lib目录。

使用问题

问题1:VLD 1.9

    在vista下使用vld的使用,老是出现错误没法正常工做,后来通过搜索,在http://www.codeproject.com/KB/applications/visualleakdetector.aspx
上的评论中找到了解决的方法:
    评论“Solution forrunning 1.9 beta on Visual Studio 2008 with Vista ”给出了解决方法:
评论1:
VLD keptcrashing when trying to use 1.9g beta on Windows Vista, visual studio 2008. Itried all the suggestions on here and nothing worked. But I finally figured itout.
when you make a project in visual C++ 2008,it sets some strange advanced Linker properties that cause VLD to crash:
I changedLinker->Advanced->Randomized Base Address from Enable Image Randomization(/DYNAMICBASE) to Disable Image Randomization (/DYNAMICBASE:NO)
Then I changed Linker->Advanced->DataExecution Prevention from Image is compatible with DEP (/NXCOMPAT) to Default
And now it works perfectly
Please let me know if this helped you!It'll make me feel better for spending a whole day trying to get it working!
 -Nadav
评论2:
The base address randomization seems to benot necessary. Just disable DEP.
   大体的意思是说,只须要禁用DEP便可,
   在工程的“属性”->“连接器”->“高级”->数据执行保护(DEP),设为“默认”(default)或者“映像与 DEP 不兼容(/NXCOMPAT:NO)“ 便可。(修改后好像不可用)。
  注:这个选项只针对Vista有效!!!

问题2:VLD 2.2.3

    在项目中使用了visual leak detector,调试时程序没法启动报错“应用程序正常启动失败(0xc0150002)”。
    解决流程:
    查看vs输出信息最后一条是:
Theprogram '[3980] MobileSignalAnalyzer.exe: Native' has exited with code-1072365566 (0xc0150002)
    在网上多方查找有:
http://blog.csdn.net/evilswords/article/details/5698851
http://blog.csdn.net/brook0344/article/details/6685724
    这两篇有解决办法,就是把VLD中的这两个复制到执行文件夹下就正常了
Microsoft.VC90.CRT.manifest
Microsoft.DTfW.DHL.manifest
    产生缘由:
    VC200三、VC200五、VC2008及其后续版本,对底层最基本的CRT、MFC、ATL库都进行了重构,为了不不一样版本的库引发冲突,重构后的库文件通常放在C://windows/WinSxS 文件夹中,并用特定的文件夹/文件名称进行标识;
    与VC6不一样, VC200三、VC200五、VC2008及其后续版本,引入了manifest清单的概念,即应用程序编译后会同时生成对应的.manifest文件,并将该.manifest文件做为资源编译到dll或者exe中去。.manifest文件其实是一个XML格式的文本文件,里面记录了dll或exe中要引用的CRT、MFC、ATL库的版本和名称。VC6编译的应用程序对CRT、MFC、ATL的dll都是直接调用,而VC200三、VC200五、VC2008编译的程序都是先查询编译到资源中的manifest中的记录,而后按照记录提供的版本和名称去搜寻对应的CRT、MFC、ATL库以及随库发布的.manifest文件,搜寻的路径包括当前目录、C://windows/WinSxS等等,若是没有找到对应的库文件,则提示“应用程序正常初始化失败”。
相关文章
相关标签/搜索