小程序做视频网站,网站建设有什么技术,免费做 爱视频网站,网站推广渠道类型在 Visual C 2022 中使用 Visual Leak Detector 1 问题描述1.1 内存泄漏的困扰和解决之道1.2 内存泄漏检测工具的选择1.3 VLD的现状 2 安装和设置VLD的环境变量2.1 安装VLD文件2.2 VLD安装后的目录和文件说明2.2.1 include子目录说明2.2.2 lib子目录说明2.2.2.1 目录整理 2.2.3… 在 Visual C 2022 中使用 Visual Leak Detector 1 问题描述1.1 内存泄漏的困扰和解决之道1.2 内存泄漏检测工具的选择1.3 VLD的现状 2 安装和设置VLD的环境变量2.1 安装VLD文件2.2 VLD安装后的目录和文件说明2.2.1 include子目录说明2.2.2 lib子目录说明2.2.2.1 目录整理 2.2.3 bin子目录说明2.2.3.1 目录整理 2.3 设置VLD的环境变量 3 VLD的使用方法3.1 在Visual C开发环境中设置VLD的相关设置3.1.1 头文件目录引用3.1.2 导入库设置3.1.3 在Post-Build Event中拷贝VLD动态链接库到生成目录 3.2 在程序中调用VLD进行内存泄漏分析3.2.1 调用VLD进行内存泄漏分析示例 4 缩略语 1 问题描述
1.1 内存泄漏的困扰和解决之道
在C/C程序开发过程中开发者受益于C/C的强大与此同时也承受着C/C程序开发的额外风险。像Java、C#这类带GC内存垃圾回收的编程语言在内存管理方面给开发者提供了“保姆级”的封装开发者不用太关注内存泄漏问题1。但是C/C的哲学是把更多的控制权交给了开发者在给了开发者更多的自由的同时也要求开发者承担更多的责任。
C/C程序的常见风险之一就是内存泄漏2问题。如果缺乏经验的或者大意的开发者对指针、内存的操作不当容易引起内存泄漏、缓冲区溢出等问题轻则造成程序预期之外的运行缺陷重则被攻击者作为漏洞加以利用造成网络安全问题。
内存泄漏的问题一旦发生问题的定位往往比较困难所以有经验的工程师总结出了解决内存泄漏问题的“黄金法则”就是“越早发现越好定位”。比如说如果程序的上一个版本并不存在显式的内存泄漏问题然后刚才开发者进行了一个小小的改动结果发生了内存泄漏那么内存泄漏问题的根源Root cause极有可能就在刚才的那部分代码更改中。如果开发者立即发现了内存泄漏问题然后马上怀疑到这部分代码变动进行范围较小的排查通常能够比较快地发现和解决内存泄漏问题。但如果开发者没有及时发现内存泄漏的发生继续进行后续的开发在进行了好几轮代码迭代之后才发现存在内存泄漏问题此时要想再找出内存泄漏的根源并予以解决很显然难度要大得多。
所以在C/C程序开发过程中有效地监测内存泄漏的发生常常是一项必须被满足的技术需求。所以就诞生了许多内存泄漏检测工具。
当然工具永远不能取代有经验的开发者。掌握RAII之类的程序设计原理和技巧在写代码的过程中就避免内存泄漏等问题而不是先“写Bug”再“解Bug”是每一位C/C开发者的基本职业修养之一。
1.2 内存泄漏检测工具的选择
针对不同的软件运行平台有不同的内存泄漏检测工具可以选择。比如说在Linux操作系统平台上知名的内存泄漏检测工具有 Valgrind、Memleax 等在Windows操作系统平台有Deleaker、VLDVisual Leak Detector等工具。
本文针对Windows操作系统平台上最好用的内存泄漏检测工具VLD。其它的工具要么相比VLD来说使用更繁琐要么要收费而且费用还不便宜比如Deleaker要么又繁琐又贵但VLD实属一股清流好用还免费。
1.3 VLD的现状
VLD官方的版本目前停留在2.5.13发布日期是2017年10月17日支持Windows 10其VS插件支持到Visual C 2015。网址是https://kinddragon.github.io/vld/
广大开发者当然不甘心 VLD 就停留在 2.5.1 版本所以在 github 上有另外一个分支的VLD目前最高稳定版本是 2.7.0发布于2021年9月13日。其插件支持 Visual C 2019 16.7.5。
那么 Visual Studio 2022Visual C 17就没有VLD的插件支持了怎么办呢
实际上使用VLD不需要依赖于它的VS插件。本文接下来介绍的方法就是不依赖VLD的插件在Visual C 2022开发环境中使用VLD。实际上由于不依赖IDE的插件所以本文介绍的方法适用于在Windows平台任意开发环境中使用VLD4哪怕将来出现了VS2023、VS2024本文的方法也同样适用。
2 安装和设置VLD的环境变量
2.1 安装VLD文件
VLD 2.7.0版的安装文件如图 2-1 所示。 图 2-1VLD 2.7.0版的安装文件
如图 2-2 所示安装VLD到建议的目录D:\App\Dev\VLD\v2.7.0 图 2-2安装VLD到指定路径
2.2 VLD安装后的目录和文件说明
如图 2-2 所示VLD安装后在安装目录中生成了以下3个文件夹见表 2-1。我们在应用VLD的过程中仅仅和这3个文件夹里的文件打交道。 表 2-1VLD安装目录的子目录说明
序号文件夹名称说明1include使用VLD所需的C语言头文件所在文件夹2lib隐式调用静态加载VLD的动态链接库所需的导入库3binVLD的动态链接库
2.2.1 include子目录说明
include子目录中包括调用VLD动态链接库所需的所有头文件。
2.2.2 lib子目录说明
lib子目录中包括隐式调用静态加载VLD的动态链接库所需的导入库vld.lib。
2.2.2.1 目录整理 图 2-4lib目录整理
如图 2-4 所示将lib子目录中的Win64文件夹重命名为x64。
2.2.3 bin子目录说明
bin子目录中包含VLD的动态链接库vld_*.dll5以及动态链接库运行时涉及到的 “.pdb6” 文件和依赖的动态库dbghelp.dll7。
2.2.3.1 目录整理 图 2-5bin目录整理
2.3 设置VLD的环境变量
安装好VLD之后为了能够方便地使用VLD进行相关环境变量设置。 图 2-6设置VLD相关的环境变量
如图 2-6 所示在系统环境变量中新建环境变量VLD_Root变量值设置为VLD的安装路径即D:\App\Dev\VLD\v2.7.0
3 VLD的使用方法
3.1 在Visual C开发环境中设置VLD的相关设置
通过宏定义使得程序在Debug模式下调用VLD进行内存泄漏检查。在Release模式下不调用VLD8。
使用VLD的方法如下所述一共包括3步
3.1.1 头文件目录引用
如图 3-1所示在 MSVC 的 C/C 工程属性中针对All Configurations和All Platforms设置 C/C | General | Additional Include Directories增加
$(VLD_Root)\include图 3-1引入VLD的头文件所在路径
3.1.2 导入库设置
如图 3-2在MSVC的C/C工程属性中针对Configuration: Debug和All Platforms设置Linker | General | Additional Library Directories增加
$(VLD_Root)\lib\$(Platform)图 3-2引入VLD引入库所在路径
如图 3-3在MSVC的C/C工程属性中针对Configuration: Debug和All Platforms设置 Linker | Input | Additional Dependencies增加
vld.lib图 3-3引入VLD导入库
3.1.3 在Post-Build Event中拷贝VLD动态链接库到生成目录
如图 3-4在MSVC的C/C工程属性中针对Configuration: Debug和All Platforms设置Build Events | Post-Build Event | Command Line增加
COPY $(VLD_Root)\bin\$(Platform)\*.* $(TargetDir)图 3-4拷贝VLD动态链接库到生成目录
3.2 在程序中调用VLD进行内存泄漏分析
在C/C应用程序的任意一个源码文件中引入一次vld.h即可实现对VLD的调用。通常的做法是在main函数所在程序源码文件中引入vld.h。显而易见对vld.h的引用当前仅当Win32平台Windows操作系统平台、Debug编译模式的运行时有效。
3.2.1 调用VLD进行内存泄漏分析示例
如代码 3-1 所示这是一个最简单的C语言应用程序的源码。
#include stdio.h
#include stdlib.hint main(int argc, char* argv[])
{printf(Hello from console application.\n);return EXIT_SUCCESS;
}代码 3-1示例一个最简单的C语言应用程序的源码
以上程序尚未加入对VLD的调用我们编译出它的x64|Debug版本并运行如图 3-5 所示该运行结果用来对随后加入VLD调用之后的运行结果进行对比。 图 3-5运行结果1
现在我们在中代码 3-1 增加对 VLD 的引用如代码 3-2 所示。
#include stdio.h
#include stdlib.h#if defined(_WIN32) defined(_DEBUG)
#include vld.h
#endifint main(int argc, char* argv[])
{printf(Hello from console application.\n);return EXIT_SUCCESS;
}代码 3-2调用 VLD
由代码 3-2 可知在程序源码中加入对VLD的调用仅仅需要引用VLD的头文件vld.h即可。为了将对vld.h的引用限定在Win32平台Windows操作系统平台、Debug编译模式我们用宏定义进行了限定如代码 3-3 所示。程序源码其它部分不需要作任何更改。
#if defined(_WIN32) defined(_DEBUG)
#include vld.h
#endif代码 3-3引用vld.h
我们编译出它的x64|Debug版本并运行如图 3-6 所示 图 3-6运行结果2
对比图 3-5 和图 3-6 的两个运行结果易知在增加了代码 3-3 中对vld.h的引用之后VLD监视了程序的运行过程并未发现任何内存泄漏。
现在我们在代码 3-1 中增加一处显而易见的内存泄漏如代码 3-4 所示然后观察运行结果的变化。
#include stdio.h
#include stdlib.h#if defined(_WIN32) defined(_DEBUG)
#include vld.h
#endifint main(int argc, char* argv[])
{int x 0;char* p NULL;x;p malloc(1);x--;printf(Hello from console application.\n);return EXIT_SUCCESS;
}代码 3-4增加显而易见的一处内存泄漏
在代码 3-4 中我们在第14行代码中申请了1个字节的堆上内存空间但整个程序直到运行结束前并未对该内存申请进行释放很显然这样就造成了1个字节的内存泄漏。现在我们编译出它的x64|Debug版本并运行观察在VLD的帮助下能否发现此处内存泄漏。运行结果如图 3-7 所示。 图 3-7运行结果3发现了1处内存泄漏
如图 3-7 所示VLD发现了程序中的1处内存泄漏并且报告如下
在堆上发现了1个字节的内存泄漏从WinNT内核对象KERNEL32到C语言运行时的启动函数CRTStartup()再到程序的入口函数main在这一层层的程序调用栈call stack中找到了内存泄漏发生的地方就是源代码的第14行定位非常精准泄漏的这1字节的内存空间其内容是啥呢实际上这个内存空间没有进行初始化所以就是操作系统默认给它的样子。。。
有图有真相。可见VLD查找内存泄漏的能力十分强大给出的内存泄漏报告信息量很大也很精准。
现在我们给VLD增加一点点任务难度我们把造成内存泄漏的代码封装一下看看VLD是否还能精准地定位造成内存泄漏的语句位置。如代码 3-5 所示。
#include stdio.h
#include stdlib.h#if defined(_WIN32) defined(_DEBUG)
#include vld.h
#endifstatic void do_something();int main(int argc, char* argv[])
{do_something();printf(Hello from console application.\n);return EXIT_SUCCESS;
}static void do_something()
{int x 0;char* p NULL;x;p malloc(1);x--;
}代码 3-5把造成内存泄漏的语句稍微封装一下
运行结果如图 3-8 所示。 图 3-8运行结果4精准定位出内存泄漏发生的代码位置
在代码 3-5 中我们把造成内存泄漏的代码封装在了do_something()函数的第24行。在图 3 8中我们可以看到VLD定位出了内存泄漏发生的代码位置是do_something()函数的第24行。VLD 工作得很好。虽然这里我只是给出了一个非常简单的例子但是在实际工作中我们的程序经常会很复杂而 VLD 始终工作得很好从未令我失望。
然后如代码 3-6 所示我们修复这一处内存泄漏看看运行结果如何。
#include stdio.h
#include stdlib.h#if defined(_WIN32) defined(_DEBUG)
#include vld.h
#endifstatic void do_something();int main(int argc, char* argv[])
{do_something();printf(Hello from console application.\n);return EXIT_SUCCESS;
}static void do_something()
{int x 0;char* p NULL;x;p malloc(1);x--;free(p);p NULL;
}代码 3-6修复内存泄漏
我们编译出它的x64|Debug版本并运行运行结果如图 3-9 所示。 图 3-9运行结果5内存泄漏的问题被修复
结果符合预期从VLD的报告中我们得到的信息是
程序运行过程在VLD的监视之下VLD没有发现内存泄漏。
4 缩略语
VLDVisual Leak DetectorVSVisual StudioMSVCMicrosoft Visual C是VS的组件。 但是这也不绝对如果使用不当Java/C#程序也会产生内存泄漏。 ↩︎ 请查阅维基百科上的“Memory Leak内存泄漏”词条链接是https://en.wikipedia.org/wiki/Memory_leak ↩︎ VLD v2.5.1 发布于2017年10月17日截止到发布本文的今天2024年6月20日https://kinddragon.github.io/vld/ 网页上仍然没有版本更新。 ↩︎ VLD 依赖于dbghelp.dll只要能支持dbghelp.dll就能支持VLD。 ↩︎ 32位VLD的动态链接库的文件名是vld_x86.dll64位VLD的动态链接库的文件名是vld_x64.dll。 ↩︎ PDBProgram Data Base文件即程序的基本数据文件是由Visual Studio对程序进行编译链接时产生的。该文件主要存储VS调试程序所需的基本信息主要包括源文件名、变量名、函数名、FPO帧指针、对应的行号等调试信息。一般情况下PDB文件是在Debug模式下才会生成但有些编译参数情况下Release模式也会产生PDB文件。 ↩︎ MSVC的Debug Help Library动态链接库详见https://learn.microsoft.com/en-us/windows/win32/debug/debug-help-library ↩︎ 实际上在Release模式下即使调用VLD也不会产生任何影响。 ↩︎