说明

使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。

1. 使用方式

在 QT 中使用 VLD 的方法可以查看另外几篇博客:

本次测试使用的环境为:QT 5.9.2MSVC 2015 32bitDebug 模式,VLD 版本为 2.5.1,VLD 配置文件不做任何更改使用默认配置,测试工程所在路径为:E:\Cworkspace\Qt 5.9\QtDemo\testVLD

2. 有一处内存泄漏时的输出报告(int 型)

写一个有一处内存泄漏的程序,如下:

#include <QCoreApplication>
#include "vld.h" void testFun()
{
int *ptr = new int(0x55345678);
printf("ptr = %08x, *ptr = %08x", ptr, *ptr);
} int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); testFun(); return a.exec();
}

程序运行时,在标准输出窗会输出以下结果:

ptr = 0127b7a0, *ptr = 55345678

程序运行结束后,检测到了内存泄漏,VLD 会输出以下报告(本例中出现一处内存泄漏),第 1~3 行显示 VLD 运行状态,第 4~21 行显示泄漏内存的详细信息,第 22~24 行总结此次泄漏情况,第 25 行显示 VLD 退出状态。

Visual Leak Detector read settings from: D:\Program Files (x86)\Visual Leak Detector\vld.ini
Visual Leak Detector Version 2.5.1 installed.
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 1 at 0x0127B7A0: 4 bytes ----------
Leak Hash: 0xEB4D3A14, Count: 1, Total 4 bytes
Call Stack (TID 22408):
ucrtbased.dll!malloc()
f:\dd\vctools\crt\vcstartup\src\heap\new_scalar.cpp (19): testVLD.exe!operator new() + 0x9 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (6): testVLD.exe!testFun() + 0x7 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (16): testVLD.exe!main()
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (74): testVLD.exe!invoke_main() + 0x1B bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (264): testVLD.exe!__scrt_common_main_seh() + 0x5 bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (309): testVLD.exe!__scrt_common_main()
f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp (17): testVLD.exe!mainCRTStartup()
KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0x11E bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xEE bytes
Data:
78 56 34 55 xV4U.... ........ Visual Leak Detector detected 1 memory leak (40 bytes).
Largest number used: 40 bytes.
Total allocations: 40 bytes.
Visual Leak Detector is now exiting.

第 1 行表示 VLD 读取的配置文件路径,可以根据路径找到该文件,然后更改里面的相关配置,获得想要的效果。

第 2 行表示 VLD 2.5.1 在程序中初始化成功。

第 3 行表示本次运行检测到了内存泄漏。

第 4 行中,Block 1 表示本块内存是在堆上分配的第 1 个内存块,0x0127B7A0 表示该内存块的首地址,与标准输出窗输出的 ptr = 0127b7a0 一致,4 bytes 表示该内存块的大小,这一行输出了泄漏内存块的地址信息和大小信息。

第 5 行中,Leak Hash: 0xEB4D3A14 是由泄漏块大小及调用堆栈信息计算出的唯一标识符,如果在报告中看到相同的 Leak Hash,这表示这些泄漏块具有相同大小和相同的调用堆栈。Count: 1 是发生泄漏的计数,使用默认配置时全部等于 1,可以将配置文件中的参数 AggregateDuplicates 设置为 yes 来合并显示具有相同 Leak Hash 值的的泄漏块信息。Total 4 bytes 是此内存块的泄漏大小,与第 4 行一致。这一行输出了泄漏内存块的唯一标识符、泄漏频次、大小信息。

第 6 行中,Call Stack 表示接下来的几行是产生泄漏的调用堆栈,(TID 22408) 表示产生此内存泄漏块函数所在线程的 TID22408,据此来指示发生内存泄漏的线程。在调试多线程程序时,TID 信息很有帮助,它能帮助确定泄漏所在线程。

第 7 行中,ucrtbased.dll 是一个系统库,提供了各种标准 C 和 C++ 函数的实现,包括 malloc(),这个函数用于在运行时动态分配内存。它处于调用栈顶,表示此内存块是使用 ucrtbased.dll 库中的 malloc() 分配的,然后传递给第 8 行 testVLD.exe 程序中的 operator new()

第 8 行中,f:\dd\vctools\crt\vcstartup\src\heap\new_scalar.cpp 是从 MSVC 中获取的调试信息,这个路径是内置在 Visual C++ Runtime Library 中的,并不代表 new_scalar.cpp 的真实路径,它的真实路径一般在 Visual Studio 的安装目录下。在我电脑上:

  • Visual Studio 2015 使用的 new_scalar.cpp 文件真实路径为 C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\new_scalar.cpp
  • Visual Studio 2019 使用的 new_scalar.cpp 文件真实路径为 D:\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.23.28105\crt\src\vcruntime\new_scalar.cpp

在不同系统上的相同版本的 Visual C++ Runtime Library 中,这个内置路径通常是一样的。(19) 表示 operator new() 函数中分配内存的代码位于 new_scalar.cpp 文件的第 19 行,最后面的 + 0x9 bytes 表示从 operator new() 函数开始到导致泄漏产生的指令的内存偏移量,这些信息在调试时很有用,可以帮助快速定位到确切代码行。

第 9 行中,e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (6): testVLD.exe!testFun() + 0x7 bytes 表示 main.cpp 位于 e:\cworkspace\qt 5.9\qtdemo\testvld 路径下,这与项目实际路径是一致的,差别只是 VLD 将其全部转成了小写字母形式,testFun() 函数中分配内存的代码位于 main.cpp 的第 6 行,这与实际情况完全一致。最后面的 + 0x7 bytes 表示从 testFun() 函数开始到导致泄漏产生的指令的内存偏移量,这个信息据说是多用于汇编调试中,与实际是否能对上还没仔细研究过。

第 10 行中,e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (16): testVLD.exe!main() 表示 main() 函数中分配内存的代码位于 main.cpp 的第 16 行,没有提供指令的内存偏移信息,这与实际情况(第 14 行)有些差异,不过第 14 与第 16 行之间并没有别的代码,造成这种差异的原因有待深究,但对于定位泄漏点所在位置已经够用了。

第 11~14 行,跟踪显示了启动程序所调用的函数链,其中 mainCRTStartup() 函数是入口点。

第 15~17 行,跟踪显示了程序启动时所调用的 Windows 操作系统函数,BaseThreadInitThunk() 一般都会出现在调用栈底,它是 Windows 进程中所有用户模式线程的入口点,系统调用它在进程中启动一个新线程,并由它调用程序的主函数。ntdll.dll 中的 RtlGetAppContainerNamedObjectPath() 函数被调用了两次,但指令的内存偏移量不同(分别是 0x11E bytes0xEE bytes),这也是一个 Windows 操作系统函数,用于检索与进程相关的应用程序容器名称,由 Windows 系统的各个部分和其他需要知道应用程序容器名称的程序调用,关于 Windows 容器的介绍,可以查看 Microsoft Windows 和容器

第 18~19 行,分别用十六进制及 ASCII 字符显示了泄漏内存块中信息,内存初始化时赋的初始值为 0x55345678,计算机默认使用小端字节序,因此在内存中各字节的十六进制初始值分别为 78 56 34 55,转化为十进制数,0x780x560x340x55 分别为 120、86、52、85,查找 ASCII 码表,可得这四个字节对应的 ASCII 字符分别为 xV4U,与 VLD 的输出完全一致。VLD 在显示内存内容时,每行最多显示 16 个字节,但这次只泄漏了 4 个字节,因此在显示上第 19 行中间有 12 个字节的空白位,行尾有 12 个占位点(.... ........)。

第 22 行,表示本次运行检测到 1 处内存泄漏,泄漏的总大小为 40 bytes,这里面不光包含用于 int 存储的 4 bytes,还包含用于管理追踪这块内存的另外 36 bytes,因此,虽然代码只请求了 4 bytes 的内存,但程序实际上为此分配了 40 bytes 的内存。可以查看以下资料,这两个网站内容一样,第一个是国外源博客,第二个是国内某爬虫网站盗取的博客。(实际测试发现,使用 32 bit 的编译器时,这个管理头的大小为 36bytes,当使用 64 bit 的编译器时,大小变为 52bytes。)

第 23 行,表示本次运行中单次分配的最大内存大小,为 40 bytes,原因看前一条。在实际使用过程中,这个 Largest number used 有时候跟实际情况对不上,又好像表示本次运行中分配的连续堆内存最大宽度,有兴趣的可以深入研究。

第 24 行,表示本次运行中堆上分配内存的总大小,为 40 bytes,即代码申请 int4 bytes,和管理头占用的 36 bytes

第 25 行,表示 VLD 正常退出。

在程序的第 6 行加个断点,按 F5 进入调试状态,结果如下,调用堆栈中各函数的名称、所属文件、所在行号、调用顺序都和 VLD 一致。

3. 有一处内存泄漏时的输出报告(int 数组型)

写一个有一处内存泄漏的程序,如下:

#include <QCoreApplication>
#include "vld.h" void testFun()
{
int *ptr = new int[10];
ptr[0] = 0x64568932;
printf("ptr = %08x", ptr);
} int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); testFun(); return a.exec();
}

程序运行时,在标准输出窗会输出以下结果:

ptr = 00ab4340

程序运行结束后,检测到了内存泄漏,VLD 会输出以下报告(本例中出现一处内存泄漏),第 1~3 行显示 VLD 运行状态,第 4~23 行显示泄漏内存的详细信息,第 24~26 行总结此次泄漏情况,第 27 行显示 VLD 退出状态。

Visual Leak Detector read settings from: D:\Program Files (x86)\Visual Leak Detector\vld.ini
Visual Leak Detector Version 2.5.1 installed.
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 1 at 0x00AB4340: 40 bytes ----------
Leak Hash: 0x39CB72AB, Count: 1, Total 40 bytes
Call Stack (TID 29256):
ucrtbased.dll!malloc()
f:\dd\vctools\crt\vcstartup\src\heap\new_array.cpp (15): testVLD.exe!operator new[]() + 0x9 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (6): testVLD.exe!testFun() + 0x7 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (17): testVLD.exe!main()
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (74): testVLD.exe!invoke_main() + 0x1B bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (264): testVLD.exe!__scrt_common_main_seh() + 0x5 bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (309): testVLD.exe!__scrt_common_main()
f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp (17): testVLD.exe!mainCRTStartup()
KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0x11E bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xEE bytes
Data:
32 89 56 64 CD CD CD CD CD CD CD CD CD CD CD CD 2.Vd.... ........
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........
CD CD CD CD CD CD CD CD ........ ........ Visual Leak Detector detected 1 memory leak (76 bytes).
Largest number used: 76 bytes.
Total allocations: 76 bytes.
Visual Leak Detector is now exiting.

输出与 [2. 有一处内存泄漏时的输出报告(int 型)](#2. 有一处内存泄漏时的输出报告(int 型)) 基本类似,这里提几个不同点:

第 4~5 行中,40 bytes 与代码请求的内存量大小相同,即 sizeof(int) * 10 = 40

第 8 行,表示 operator new[]() 函数中分配内存的代码位于 new_array.cpp 文件的第 15 行,这与前面的 operator new() 函数及 new_scalar.cpp 文件不同,实际使用时可以根据这一点来判断泄漏形式,是数组还是标量

第 19~21 行,十六进制数 32 89 56 64 的十进制表示为 50 137 86 100,其中 50 86 100 对应的 ASCII 字符分别为 2 V d,是可以输出显示的字符,但 137 超过了 127,不属于 ASCII 标准字符集,属于 ASCII 扩展字符集,无法直接在界面上显示,因此仍以 "." 英文句点来代替。此外,未初始化内存单个字节的值都为 CD,对应的十进制数为 205,这是 Microsoft's C++ debugging runtime library 自动初始化的结果。通常,在 Debug 模式下,MSVC 会把未初始化的栈内存全部填充成 0xCC ,当成字符串看就是”烫烫烫烫……“;同时会把未初始化的堆内存全部填充成 0xCD ,当成字符串看就是“屯屯屯屯……”。实际使用时可以根据这一点来判断是否对泄漏内存赋了初始值。关于 0xCD 这一特殊十六进制数,更详细的可以查看以下资料,国内可能无法直接访问:

第 24~26 行中,76 bytes 包含有申请 int[10]40 bytes,和管理头占用的 36 bytes

【Visual Leak Detector】QT 中 VLD 输出解析(二)的更多相关文章

  1. Cocos开发中性能优化工具介绍之Visual Studio内存泄漏检测工具——Visual Leak Detector

    那么在Windows下有什么好的内存泄漏检测工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测功能,我们可以使用第三方工具Visual Leak Detector(以下简 ...

  2. VisualStudio 怎么使用Visual Leak Detector

    VisualStudio 怎么使用Visual Leak Detector 那么在Windows下有什么好的内存泄漏检测工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测 ...

  3. vld(Visual Leak Detector) 内存泄露检测工具

    初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复 杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题.内存 ...

  4. VS2017 编译 Visual Leak Detector + VLD 使用示例

    起因 一个Qt5+VS2017的工程,需要进行串口操作,在自动时发现一段时间软件崩溃了,没有保存log,在 debug 的时候发现每运行一次应用占据的内存就多一点,后来意识到是内存泄漏了.这个真是头疼 ...

  5. 使用Visual Leak Detector for Visual C++ 捕捉内存泄露

    什么是内存泄漏? 内存泄漏(memory leak),指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段 ...

  6. 使用Visual Leak Detector检测内存泄漏[转]

      1.初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题 ...

  7. Cocos性能优化工具的开发介绍Visual Studio内存泄漏检测工具——Visual Leak Detector

    然后,Windows下有什么好的内存泄漏检測工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检測功能.我们能够使用第三方工具Visual Leak Detector(下面简 ...

  8. Visual Leak Detector原理剖析

    认识VLD VLD(Visual Leak Detector)是一款用于Visual C++的开源内存泄漏检测工具,我们只需要在被检测内存泄漏的工程代码里#include “vld.h”就可以开启内存 ...

  9. 关于Visual Leak Detector的配置与使用 (测试vector 引起的内存泄漏问题)

    之前在做一个音频特征提取的批量处理程序,老是出现内存泄露问题,用Visual Leak Detector(VLD)工具做了下检测,检测出了一些问题,解决后还是会有问题.之后继续排查,因为我的代码中,大 ...

  10. Visual Leak Detector 2.2.3 Visual C++内存检测工具

      Visual Leak Detector是一款免费的.健全的.开源的Visual C++内存泄露检测系统.相比Visual C++自带的内存检测机制,Visual Leak Detector可以显 ...

随机推荐

  1. AX2012 使用HTML自定义popup内样式

    在Class Box下新增方法如下: public client static DialogButton yesNoHTML( str _text, DialogButton _defaultButt ...

  2. Vue中的样式穿透,修改element-ui组件样式不生效

    在Vue项目中用的比较多的就是组件,为了实现组件的样式模块化.我们通常会在style标签中添加一个scoped属性,这样css样式只能作用于当前的Vue组件.使组件之间的样式相互独立,当调用该组件的时 ...

  3. PSO 算法的变体python实现

    上演化计算课的时候老师让我们实现EOPSO算法(一种精英反向的粒子群优化算法),下面是他的算法步骤: 首先我们需要知道一些基础知识: (1)基础PSO算法 (2)精英反向解 import numpy ...

  4. 记录jupyter lab 多个kernel混乱的问题

    问题描述 在Anaconda下我有多个虚拟环境,其中一个叫d2l,由于pytorch版本和cuda算力不匹配,重新create了一个环境:d2l_new.然后环境配置好了之后激活环境,启动jupyte ...

  5. Python pexpect 库的简单使用

    一.Python pexpect 库的使用 在终端中许多命令都有与用户交互的场景,例如切换用户时需要手动输入密码,安装应用有时要输入默认配置等.这对 shell 自动化脚本十分不便.expect 命令 ...

  6. 关于数据传递 json

    关于这几种语言的json 操作 Lua local cjson2 = require "cjson" local lua_object = { ["name"] ...

  7. vite设置跨域

    1. vite.config.ts或者vite.config.js文件 server: { port: 3001, host: '0.0.0.0', open: true, proxy: { // 代 ...

  8. Kicad使用技巧--原理图复用

    平时主要使用AD.kicad和立创eda,最近在琢磨一下各个软件的使用技巧,提升一下画图效率,首先想到的就是原理图复用,我想象中的原理图复用应该像AutoCAD的图块功能一样. 这次先说kicad,摸 ...

  9. __declspec(dllimport) 和 __declspec(dllexport)的使用详解、以及 XX_API 的含义

    1. C++代码里调用别人的库.或者写库给别人用.大概有如下的方法(只讨论windows系统的情况): ---- a) 提供头文件 h . 静态库 lib  -- > 静态链接 ---- b) ...

  10. 002基本的Dos命令

    002基本的Dos命令 1.开启Dos控制台的几种方式 几种打开CMD的方式: 直接在菜单中搜索"命令提示符". Win+R,输入cmd.(推荐) 在任意文件夹下面,按住Shift ...