在 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.mzph.cn/diannao/30642.shtml

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

相关文章

uniapp app一键登录

一键登录不需要单独写页面&#xff0c;uniapp 有原生的页面 第一步&#xff0c;登录Dcloud后台》我的应用》点击应用名称 填写完点击 uniCloud模块新建一个服务空间》选择免费 , 创建完点击一键登录&#xff0c;添加应用&#xff0c;这个需要审核&#xff0c;“大概一天左右”…

DevOps开发工程师在软件产品发版流程中的角色与工作

目录 引言 一、需求分析阶段 1.1 协助需求收集与分析 1.2 制定环境需求 二、开发阶段 2.1 搭建开发环境 2.2 自动化构建 2.3 代码质量检查 三、测试阶段 3.1 自动化测试 3.2 测试报告生成 3.3 性能测试与监控 四、部署阶段 4.1 部署策略制定 4.2 部署自动化 4.3…

ORM框架SQLAlchemy

ORM框架SQLAlchemy 目录 ORM框架SQLAlchemy介绍安装架构连接数据库1. PostgreSQL2. MySQL3. Oracle4. Microsoft SQL Server5. SQLite 创建连接池原生Python操作数据库SQLAlchemy的ORM操作创建表外键字段的创建一对多多对多 ORM操作增删改查查询添加删除修改 介绍 sqlalchemy是…

帝国CMS二次开发H5手游如何让同一个url 不同的模板

帝国CMS在二次开发《72游戏网》的时候&#xff0c;H5手游如何让同一个url 不同的模板 比如&#xff1a;www.72yy.com/you/11935.html 是H5游戏宣传页 由于很多H5游戏和网页游戏都是需要使用iframe来嵌套使用的 利于自己网站SEO收录优化 那么就再复制一套程序 用二级目录或者二…

三菱汽车常用油封类型

油封的功能 油封在防止润滑剂泄漏和污染物进入方面起着关键作用&#xff0c;从而确保各种机械部件的顺利运行。它们存在于发动机、变速箱和差速器等关键部位。本文概述了三菱汽车常用的油封类型&#xff0c;详细介绍了它们的应用及其带来的好处。 一、三菱汽车的主要油封类型…

一种基于非线性滤波过程的旋转机械故障诊断方法(MATLAB)

在众多的旋转机械故障诊断方法中&#xff0c;包络分析&#xff0c;又称为共振解调技术&#xff0c;是目前应用最为成功的方法之一。首先&#xff0c;对激励引起的共振频带进行带通滤波&#xff0c;然后对滤波信号进行包络谱分析&#xff0c;通过识别包络谱中的故障相关的特征频…

【Linux基础IO】重定向以及原理分析

我们先来看下面一个情况&#xff1a; #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define filename "text.txt"int main(){close(1);//关…

[Linux] 系统管理

全局配置文件 用户个性化配置 配置文件的种类 alias命令和unalias命令 进程管理 进程表

使用Docker在Mac上部署OnlyOffice,预览编辑word、excel、ppt非常好

前端编辑word、ppt文档&#xff0c;开源免费方案并没有找到合适的&#xff0c;像wps、石墨文档都是自研的方案。实现过程中wps采用的svg方案&#xff0c;而石墨文档采用的是canvas&#xff0c;它们均是自己来实现编辑器&#xff0c;不依赖浏览器提供的编辑器&#xff08;conten…

嵌入式Linux:Linux系统中文件类型

目录 1、普通文件 2、目录文件 3、字符设备文件 4、块设备文件 5、符号链接文件 6、套接字文件 7、管道文件 8、stat命令和ls命令 8.1、stat命令 8.2、ls命令 9、stat、fstat、lstat函数 9.1、stat函数 9.2、fstat函数 9.3、lstat函数 在Windows系统中&#xff0…

【Python】已完美解决:(Python键盘中断报错问题) KeyboardInterrupt

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例&#xff08;结合实战场景&#xff09;五、注意事项 已解决&#xff1a;Python中处理KeyboardInterrupt&#xff08;键盘中断&#xff09;报错问题 一、问题背景 在Python编程中&#xff0c;当我们运…

Java | Leetcode Java题解之第167题两数之和II-输入有序数组

题目&#xff1a; 题解&#xff1a; class Solution {public int[] twoSum(int[] numbers, int target) {int low 0, high numbers.length - 1;while (low < high) {int sum numbers[low] numbers[high];if (sum target) {return new int[]{low 1, high 1};} else i…

「前端+鸿蒙」鸿蒙应用开发-组件状态管理

在鸿蒙应用开发中,组件状态管理是确保UI与数据同步更新的重要概念。状态(State)是组件内部数据的集合,它可以影响组件的渲染输出。以下是组件状态管理的入门、深入和实战介绍,以及示例代码。 组件状态管理 - 入门 入门阶段,你需要了解状态是什么以及如何使用状态来更新U…

人工智能指数报告

2024人工智能指数报告&#xff08;一&#xff09;&#xff1a;研发 前言 全面分析人工智能的发展现状。 从2017年开始&#xff0c;斯坦福大学人工智能研究所&#xff08;HAI&#xff09;每年都会发布一份人工智能的研究报告&#xff0c;人工智能指数报告&#xff08;AII&…

数据结构:快速排序,归并排序,计数排序的实现分析

✨✨小新课堂开课了&#xff0c;欢迎欢迎~✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 小新的主页&#xff1a;编程版小新-CSDN博客 1.快排 1.1算法思想 选择一个基准元素&#xff0c;…

【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】008 - make distclean 命令解析

【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】008 - make distclean 命令解析 一、make V=1 distclean 命令解析系列文章汇总:《【OpenHarmony4.1 之 U-Boot 源码深度解析】000 - 文章链接汇总》 本文链接:《【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】008 - mak…

服务器SSH 免密码登录

1. 背景 为了服务器的安全着想&#xff0c;设置的服务器密钥非常长。但是这导致每次连接服务器都需要输入一长串的密码&#xff0c;把人折腾的很痛苦&#xff0c;所以我就在想&#xff0c;能不能在终端SSH的时候无需输入密码。 windows 可以使用 xshell 软件&#xff0c;会自…

安装react之nvm版本低引起的问题

1.背景 准备搭建一个react&#xff0c;然后看官网文档 创建项目&#xff0c;使用命令行 npx create-next-applatest 创建项目的流程都是正常的。当我准备运行项目的时候&#xff0c;报错了 原先的报错没有了&#xff0c;从网上找了一个类似的 重要的内容是&#xff1a;当前…

echarts警告:There is a chart instance already initialize on the dom

创建一个echarts图表的代码封装成一个方法&#xff0c;一旦多次调用这个方法创建图表的时候使用的dom节点都是同一个&#xff0c;每次都会init一个echarts实例&#xff0c;就会在控制台报如下警告:There is a chart instance already initialize on the dom&#xff08;在dom上…

锂电池寿命预测 | Matlab基于ARIMA的锂电池寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于ARIMA的锂电池寿命预测 NASA数据集&#xff0c;B0005号电池&#xff0c;选择前110个数据训练&#xff0c;后58个数据测试预测。程序包含去趋势线、差分、平稳化及AIC准则判定p和q。命令窗…