在 Visual Studio 2022 (Visual C++ 17) 中使用 Visual Leak Detector

在 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、VLD(Visual 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 2022(Visual 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-1:VLD 2.7.0版的安装文件
图 2-1:VLD 2.7.0版的安装文件

如图 2-2 所示,安装VLD到建议的目录:D:\App\Dev\VLD\v2.7.0
图 2-2:安装VLD到指定路径
图 2-2:安装VLD到指定路径

2.2 VLD安装后的目录和文件说明

如图 2-2 所示,VLD安装后,在安装目录中生成了以下3个文件夹,见表 2-1。我们在应用VLD的过程中,仅仅和这3个文件夹里的文件打交道。
表 2-1:VLD安装目录的子目录说明

序号文件夹名称说明
1include使用VLD所需的C语言头文件所在文件夹
2lib隐式调用(静态加载)VLD的动态链接库所需的导入库
3binVLD的动态链接库

2.2.1 include子目录说明

include子目录中包括调用VLD动态链接库所需的所有头文件。
图 2-3:include子目录

2.2.2 lib子目录说明

lib子目录中包括隐式调用(静态加载)VLD的动态链接库所需的导入库vld.lib

2.2.2.1 目录整理

图 2-4:lib目录整理
图 2-4:lib目录整理

如图 2-4 所示,将lib子目录中的Win64文件夹重命名为x64

2.2.3 bin子目录说明

bin子目录中包含VLD的动态链接库vld_*.dll5,以及动态链接库运行时涉及到的 “.pdb6” 文件和依赖的动态库dbghelp.dll7

2.2.3.1 目录整理

图 2-5:bin目录整理
图 2-5:bin目录整理

2.3 设置VLD的环境变量

安装好VLD之后,为了能够方便地使用VLD,进行相关环境变量设置。
图 2-6:设置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 ConfigurationsAll Platforms,设置 C/C++ | General | Additional Include Directories,增加:

$(VLD_Root)\include

图 3-1:引入VLD的头文件所在路径
图 3-1:引入VLD的头文件所在路径

3.1.2 导入库设置

如图 3-2,在MSVC的C/C++工程属性中,针对Configuration: DebugAll Platforms,设置Linker | General | Additional Library Directories,增加:

$(VLD_Root)\lib\$(Platform)

图 3-2:引入VLD引入库所在路径
图 3-2:引入VLD导入库所在路径

如图 3-3,在MSVC的C/C++工程属性中,针对Configuration: DebugAll Platforms,设置 Linker | Input | Additional Dependencies,增加:

vld.lib

图 3-3:引入VLD导入库
图 3-3:引入VLD导入库

3.1.3 在Post-Build Event中拷贝VLD动态链接库到生成目录

如图 3-4,在MSVC的C/C++工程属性中,针对Configuration: DebugAll Platforms,设置Build Events | Post-Build Event | Command Line,增加:

COPY "$(VLD_Root)\bin\$(Platform)\*.*" "$(TargetDir)"

图 3-4:拷贝VLD动态链接库到生成目录
图 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.h>int 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-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-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:运行结果3:发现了1处内存泄漏

如图 3-7 所示,VLD发现了程序中的1处内存泄漏,并且报告如下:

  1. 在堆上发现了1个字节的内存泄漏;
  2. 从WinNT内核对象KERNEL32,到C语言运行时的启动函数CRTStartup(),再到程序的入口函数main,在这一层层的程序调用栈(call stack)中,找到了内存泄漏发生的地方,就是源代码的第14行!(定位非常精准)
  3. 泄漏的这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-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:内存泄漏的问题被修复
图 3-9:运行结果5:内存泄漏的问题被修复

结果符合预期,从VLD的报告中我们得到的信息是:

  1. 程序运行过程在VLD的监视之下;
  2. VLD没有发现内存泄漏。

4 缩略语

  • VLD:Visual Leak Detector
  • VS:Visual Studio
  • MSVC:Microsoft Visual C++,是VS的组件。

  1. 但是这也不绝对,如果使用不当,Java/C#程序也会产生内存泄漏。 ↩︎

  2. 请查阅维基百科上的“Memory Leak(内存泄漏)”词条,链接是:https://en.wikipedia.org/wiki/Memory_leak ↩︎

  3. VLD v2.5.1 发布于2017年10月17日,截止到发布本文的今天(2024年6月20日),https://kinddragon.github.io/vld/ 网页上仍然没有版本更新。 ↩︎

  4. VLD 依赖于dbghelp.dll,只要能支持dbghelp.dll,就能支持VLD。 ↩︎

  5. 32位VLD的动态链接库的文件名是vld_x86.dll,64位VLD的动态链接库的文件名是vld_x64.dll。 ↩︎

  6. PDB(Program Data Base)文件,即程序的基本数据文件,是由Visual Studio对程序进行编译链接时产生的。该文件主要存储VS调试程序所需的基本信息,主要包括源文件名、变量名、函数名、FPO(帧指针)、对应的行号等调试信息。一般情况下PDB文件是在Debug模式下才会生成,但有些编译参数情况下Release模式也会产生PDB文件。 ↩︎

  7. MSVC的Debug Help Library动态链接库,详见:https://learn.microsoft.com/en-us/windows/win32/debug/debug-help-library ↩︎

  8. 实际上在Release模式下,即使调用VLD,也不会产生任何影响。 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/355431.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

感恩的力量!美洲杯魔幻提前预告 阿根廷 ——早读(逆天打工人爬取热门微信文章解读)

梅西还能不能提&#xff1f; 引言Python 代码第一篇 洞见 感恩的力量&#xff08;深度好文&#xff09;第二篇 视频新闻结尾 引言 早上早起 昨天晚上1点多才睡 这几天都是 明明很早就准备上床睡觉 但是就是忍不住 吃根雪糕 喝个小饮料 看看最近的欧洲杯比赛 卒 真的是拖延症十…

【总结】ui自动化selenium知识点总结

1. 大致原理 首页安装第三方库selenium库&#xff0c; 其次要下载好浏览器驱动文件&#xff0c;比如谷歌的 chromedriver.exe&#xff0c;配置上环境变量。 使用selenium的webdriver类去创建一个浏览器驱动对象赋值叫driver&#xff0c;一个浏览器驱动对象就可以 实现 对浏…

python爬虫之selenium自动化操作

python爬虫之selenium自动化操作 需求&#xff1a;操作淘宝去掉弹窗广告搜索物品后进入百度回退又前进 selenium模块的基本使用 问题&#xff1a;selenium模块和爬虫之间具有怎样的关联? 1、便捷的获取网站中动态加载的数据 2、便捷实现模拟登录 什么是selenium模块&#x…

【python】PyQt5初体验,窗口等组件开发技巧,面向对象方式开发流程实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

文件扫描工具哪个好?便捷的文件扫描工具推荐

对于初入职场的大学毕业生&#xff0c;申请就业补贴是一项不可忽视的福利。 它不仅能够为新生活带来经济上的缓解&#xff0c;也有助于职业生涯的顺利起步。面对申请过程中需提交的文件&#xff0c;如纸质劳动合同&#xff0c;不必烦恼。市面上众多文件扫描软件能助你一臂之力…

轮式机器人Swiss-Mile城市机动性大提升:强化学习引领未来城市物流

喜好儿小斥候消息&#xff0c;苏黎世联邦理工学院的研究团队成功开发了一款革命性的机器人控制系统&#xff0c;该系统采用强化学习技术&#xff0c;使轮式四足机器人在城市环境中的机动性和速度得到了显著提升。 喜好儿网 这款专为轮腿四足动物设计的控制系统&#xff0c;能…

一种基于图卷积创新的电场强度监测模型,原创未发表!!!

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类算法的家人&#xff0c;可关注我的VX公众号&#xff1a;python算法小当家&#xff0c;不定期会有很多免费代码分享~ 一种基于图卷积创新的电场强度监测模型&#xff0c;原创未发表…

环境配置02:CUDA安装

1. CUDA安装 Nvidia官网下载对应版本CUDA Toolkit CUDA Toolkit 12.1 Downloads | NVIDIA Developer CUDA Toolkit 12.5 Downloads | NVIDIA Developer 安装配置步骤参考&#xff1a;配置显卡cuda与配置pytorch - 知乎 (zhihu.com) 2. 根据CUDA版本&#xff0c;安装cudnn …

内容安全复习 2 - 网络信息内容的获取与表示

文章目录 信息内容的获取网络信息内容的类型网络媒体信息获取方法 信息内容的表示视觉信息视觉特征表达文本特征表达音频特征表达 信息内容的获取 网络信息内容的类型 网络媒体信息 传统意义上的互联网网站公开发布信息&#xff0c;网络用户通常可以基于网络浏览器获得。网络…

mysql8.0找不到my.ini

报错问题解释&#xff1a; MySQL 8.0 在Windows系统中通常不需要 my.ini 文件&#xff0c;因为安装程序会在 %PROGRAMDATA%\MySQL\MySQL Server 8.0\ &#xff08;通常是 C:\ProgramData\MySQL\MySQL Server 8.0\&#xff09;创建默认的配置文件。如果你的系统中找不到 my.ini…

ArcGIS查找相同图斑、删除重复图斑

​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 这次是上次 今天分享一下&#xff0c;很重要却被大家忽略的两个工具 这两个工具不仅可以找出属性…

解决电脑关机难题:电脑关不了机的原因以及方法

在使用电脑的日常生活中&#xff0c;有时会遇到一些烦人的问题&#xff0c;其中之一就是电脑关不了机。当您尝试关闭电脑时&#xff0c;它可能会停留在某个界面&#xff0c;或者根本不响应关机指令。这种情况不仅令人困惑&#xff0c;还可能导致数据丢失或系统损坏。 在本文中…

编译xlnt开源库源码, 使用c++读写excel文件

编译xlnt开源库源码,在linux平台使用c读写excel文件 下载xnlt源码 官方网站https://tfussell.gitbooks.io/xlnt/content/ 下载地址https://github.com/tfussell/xlnt 下载libstudxml开源库源码 下载地址https://github.com/kamxgal/libstudxml 下载xnlt源码 官方网站https://…

Walrus:去中心化存储和DA协议,可以基于Sui构建L2和大型存储

Walrus是为区块链应用和自主代理提供的创新去中心化存储网络。Walrus存储系统今天以开发者预览版的形式发布&#xff0c;面向Sui开发者征求反馈意见&#xff0c;并预计很快会向其他Web3社区广泛推广。 通过采用纠删编码创新技术&#xff0c;Walrus能够快速且稳健地将非结构化数…

C++的动态内存分配

使用new/delete操作符在堆中分配/释放内存 //使用new操作符在堆中分配内存int* p1 new int;*p1 2234;qDebug() << "数字是&#xff1a;" << *p1;//使用delete操作符在堆中释放内存delete p1;在分配内存的同时初始化 //在分配内存的时初始化int* p2 n…

海外云手机自动化管理,高效省力解决方案

不论是企业还是个人&#xff0c;对于海外社媒的营销都是需要自动化管理的&#xff0c;因为自动化管理不仅省时省力&#xff0c;而且还节约成本&#xff1b; 海外云手机的自动化管理意味着什么&#xff1f;那就是企业无需再投入大量的人力和时间去逐一操作和监控每一台设备。 通…

k8s学习--OpenKruise详细解释以及原地升级及全链路灰度发布方案

文章目录 OpenKruise简介OpenKruise来源OpenKruise是什么&#xff1f;核心组件有什么&#xff1f;有什么特性和优势&#xff1f;适用于什么场景&#xff1f; 什么是OpenKruise的原地升级原地升级的关键特性使用原地升级的组件原地升级的工作原理 应用环境一、OpenKruise部署1.安…

Python自动化(5)——ocr识字

Python自动化(5)——ocr识字 通过网络识字 网络识字的平台有很多&#xff0c;主要有百度以及科大讯飞&#xff0c;这里以百度智能云来简单示例。 首先需要在百度智能云上注册一个账号&#xff0c;并创建一个应用&#xff0c;网址&#xff1a; https://console.bce.baidu.com/…

高速异地组网怎么办理?

在当今信息化时代&#xff0c;跨地域的远程办公、远程教育、远程医疗等需求越来越多。而高速异地组网作为一种解决不同地区之间快速组建局域网的方法&#xff0c;被广泛应用。本文将介绍一款异地组网内网穿透产品——【天联】&#xff0c;并提供其办理流程。 【天联】组网是什…

LoRa126X系列LoRa模块:专为物联网设计而生

LoRa126X是思为无线研发的一款应用于物联网应用的LoRa 前端模块系列&#xff0c;采用 Semtech 公司的 SX1262和SX1268 芯片。该系列模块具有小体积、低功耗&#xff0c;高灵敏度等特点&#xff0c;并且严格遵循无铅工艺生产和测试流程&#xff0c;符合 RoHS 和 Reach 环保标准。…