VLD = Visual Leak Detector,是一款用于 Visual C++ 的免费的内存泄露检测工具,官网 kinddragon.github.io, GitHub 。先说优点:
- 为每个泄漏的块提供完整的堆栈跟踪,包括源文件和行号信息(如果可用)。
- 检测大多数(如果不是全部)类型的进程内内存泄漏,包括基于 COM 的泄漏和纯 Win32 基于堆的泄漏。
- 可以设置过滤指定的模块(DLL 甚至主 EXE),不参与内存泄露检查。
- 提供泄漏块的完整数据转储(十六进制和 ASCII)。
- 可自定义的内存泄漏报告:可以保存到文件或发送到调试器,并且可以包含可变的详细级别
- 可以设置内存泄露报告的级别
- 它是一个已经打包的 lib,使用时无须编译它的源代码,只需要引入头文件即可
- 他的源代码使用 GNU 许可发布,并有详尽的文档及注释。对于想深入了解堆内存管理的读者,是一个不错的选择。
再谈缺点,或者说限制性:
1、只针对 Visual C++ ,所以 Qt 的项目如果是 MinGW 编译的需要调整为 MSVCXX 构建。
2、只能检测堆(Heap)上分配的内存泄漏,不能检测资源泄露(如 GDI 等系统资源)。
总的来说,对于在 Windows 环境下用 VC++ 编译的项目,是免费且十分容易使用的非常棒的内存泄露检查库。可惜目前停留在了 2.5.1 版 (2017-10-17)。
Releases · KindDragon/vld (github.com) ,基于众所周知的原因,下不到的请移步于此:(VisualLeakDetector)vld-2.5.1-setup.exe.7z
下载解压后安装一路点击就可以了。
使用 VLD 很简单,对于 Qt 项目只需要以下三步即可(假设我们都安装在 D:\VLD 目录下):
- 复制头文件、lib文件
将 D:\VLD\下的 include 子目录下的 .h 文件都复制到 Qt 项目对应的构建库目录下对应的 include 、 lib 子目录中。
构建目录可以通过菜单 工具/选项,Kits中找到项目对应的构建套件(KIT),并找到对应的 qmake.exe所在路径,其父路径就是我们要找的构建库目录,如我这里是 E:\Qt\Qt5.12.2\5.12.2\msvc2017。
- 源码中引入 vld
包含 vld.h 头文件即可。
- 修改 .pro 项目文件(这一步,我测试不需要,也许是版本问题?)
- win32 {
CONFIG(debug, debug|release) {
# DEFINES += _DEBUG
VLD_PATH = C:/Program Files (x86)/Visual Leak Detector
INCLUDEPATH += $VLD_PATH/include
LIBS += -L$VLD_PATH/lib/Win32 -lvld
}
}
如果希望检查 DLL 的内存泄漏,则还要在每个 DLL 的至少一个源文件中包括 vld.h。
经过如上配置,跑起程序来,如果配置正确,则在【应用程序输出】窗口中,可以看到vld的输出,如下
18:14:33: Debugging starts
Visual Leak Detector read settings from: D:\VLD\vld.ini
Visual Leak Detector Version 2.5.1 installed.
退出程序,则在【应用程序输出】窗口中看到输出,如下
No memory leaks detected.
Visual Leak Detector is now exiting.
18:14:40: Debugging has finished
很好,没有泄露,让我们刻意制造一个泄露:
int * p = new int();
*p = 0xAABBFF;
qDebug() << "p address : " << p;
new 一个 int 型指针,赋值为 0xAABBFF,最后显示指针地址。测试下 VLD 是否能捕获到这个泄露,进一步,是否能显示泄露内存的数据。
跑起来程序后关闭窗口退出, VLD 显示如下,对关键数据用红色粗斜体标注,并配了#序号:
#1 p address : 0x40b95a8
WARNING: Visual Leak Detector detected memory leaks!
#2 ---------- Block 8 at 0x040B95A8: 4 bytes ----------
#3 Leak Hash: 0xF4DB2A55, Count: 1, Total 4 bytes
Call Stack (TID 11204):
ucrtbased.dll!malloc()
d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\heap\new_scalar.cpp (35): testvld.exe!operator new() + 0x9 bytes
e:\test\qt\vld\testvld\mainwindow.cpp (19): testvld.exe!MainWindow::MainWindow() + 0x10 bytes
#4 e:\test\qt\vld\testvld\main.cpp (7): testvld.exe!main() + 0xA bytes
c:\users\qt\work\qt\qtbase\src\winmain\qtmain_win.cpp (97): testvld.exe!WinMain() + 0xD bytes
d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (107): testvld.exe!invoke_main()
d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (288): testvld.exe!__scrt_common_main_seh() + 0x5 bytes
d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (331): testvld.exe!__scrt_common_main()
d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_winmain.cpp (17): testvld.exe!WinMainCRTStartup()
KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0x11E bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xEE bytes
#5 Data:
FF BB AA 00 ........ ........
Visual Leak Detector detected 1 memory leak (40 bytes).
Largest number used: 498 bytes.
Total allocations: 498 bytes.
Visual Leak Detector is now exiting.
10:21:38: Debugging has finished
#1 是我们程序中输出的地址0x40b95a8 和 VLD 检测到的(#2 Block 8 at 0x040B95A8: 4 bytes)地址一致,并且指出了数据长度为 4 字节。
#4 给出了产生泄露代码文件地址及(e:\test\qt\vld\testvld\main.cpp)、行号(7)及所在函数(testvld.exe!main())
#5 显示出泄露的内存数据片段,正是我们赋值的 0xAABBFF,只是大小端问题,显示为 FF BB AA 00 。
可见,VLD可以非常明确的捕获内存泄露并指出相关的具体信息。
上面是使用 Debug 版输出的情况,接下来看
在工程设置里加上 VLD_FORCE_ENABLE 预定义宏内存监控才会生效。
#define VLD_FORCE_ENABLE
#include "vld/vld.h"
相比 Debug 版,Release 版的信息少了很多
p address : 0xba1fa0
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 8 at 0x00BA1FA0: 4 bytes ----------
Leak Hash: 0x25F7E302, Count: 1, Total 4 bytes
Call Stack (TID 544):
ucrtbase.dll!malloc()
testvld.exe!0x0098185C()
testvld.exe!0x00981107()
testvld.exe!0x0098105C()
testvld.exe!0x009826D5()
Data:
FF BB AA 00 ........ ........
Visual Leak Detector detected 1 memory leak (4 bytes).
Largest number used: 214 bytes.
Total allocations: 214 bytes.
Visual Leak Detector is now exiting.
10:57:20: Debugging has finished
对比两份输出,缺少的部分是函数调用堆栈,也就是说在 release 版中无法获取内存泄露代码的文件路径、行号、调用函数信息。
但因为提供了内存地址、内存数据片段及大小,对问题的排查帮助还是很大的。
和 Qt 环境的主要区别就是路径的配置方式不同,以 VS2017为例,在项目右键属性中配置如下
配置头文件查找目录
配置 lib 目录
配置好后,还需要在 VLD 安装目录下 bin 下对应的平台调试文件复制到程序所在目录,如
如上配置后,源码中也是同样的 #include "vld.h",之后编译运行即可。
VLD 的内存泄露报告是可以定制的,详细的配置描述请参考 Configuration Options · KindDragon/vld Wiki · GitHub
安装目录下的vld.ini 为默认全局配置,也可以为每个程序单独配置,在应用程序目录下放置vld.ini文件即可。常用的选项如下:
VLD:选择VLD的打开与关闭。在Debug模式下运行,关闭以后会有一行VLD关闭的提示信息。默认为 on。
AggregateDuplicates:设置为 yes 时,相同地方产生内存泄漏只输出一次,但是会统计发生的次数。默认是 no 。
ReportEncoding :report 文件的编码格式,可选有 ascii, unicode,默认是 ascii 。
ReportFile :report 文件的路径。默认是 “.\memory_leak_report.txt”
ReportTo :可选有 debugger, file, both,debugger 表示输出到 debug模式下的输出窗口;file 表示只输出到文件中; both顾名思义,全都都输出。默认是 debugger 。
* 如果需要将报告输出到指定文件中,则配置 ReportFile = .\报告文件名 且 ReportTo = both。
Home · KindDragon/vld Wiki · GitHub
Qt Creator 安装 VLD_你好L的博客-CSDN博客_qt vld
QT 内存泄漏检测工具VLD 使用和安装_小何在线的博客-CSDN博客_qt vld
Configuration Options · KindDragon/vld Wiki · GitHub