【Visual Leak Detector】QT 中 VLD 输出解析(二)
说明
使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。
1. 使用方式
在 QT 中使用 VLD 的方法可以查看另外几篇博客:
本次测试使用的环境为:QT 5.9.2,MSVC 2015 32bit,Debug 模式,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)
表示产生此内存泄漏块函数所在线程的 TID
为 22408
,据此来指示发生内存泄漏的线程。在调试多线程程序时,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 bytes
和 0xEE bytes
),这也是一个 Windows
操作系统函数,用于检索与进程相关的应用程序容器名称,由 Windows
系统的各个部分和其他需要知道应用程序容器名称的程序调用,关于 Windows
容器的介绍,可以查看 Microsoft Windows 和容器。
第 18~19 行,分别用十六进制及 ASCII 字符显示了泄漏内存块中信息,内存初始化时赋的初始值为 0x55345678
,计算机默认使用小端字节序,因此在内存中各字节的十六进制初始值分别为 78 56 34 55
,转化为十进制数,0x78
、0x56
、0x34
、0x55
分别为 120、86、52、85,查找 ASCII 码表,可得这四个字节对应的 ASCII 字符分别为 x
、V
、4
、U
,与 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
。)
- Visual Leak Detector indicates 40 bytes filtered for an int *。
- Visual Leak Detector indicates 40 bytes filtered for an int *(国内)。
第 23 行,表示本次运行中单次分配的最大内存大小,为 40 bytes
,原因看前一条。在实际使用过程中,这个 Largest number used
有时候跟实际情况对不上,又好像表示本次运行中分配的连续堆内存最大宽度,有兴趣的可以深入研究。
第 24 行,表示本次运行中堆上分配内存的总大小,为 40 bytes
,即代码申请 int
的 4 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 输出解析(二)的更多相关文章
- Cocos开发中性能优化工具介绍之Visual Studio内存泄漏检测工具——Visual Leak Detector
那么在Windows下有什么好的内存泄漏检测工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测功能,我们可以使用第三方工具Visual Leak Detector(以下简 ...
- VisualStudio 怎么使用Visual Leak Detector
VisualStudio 怎么使用Visual Leak Detector 那么在Windows下有什么好的内存泄漏检测工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测 ...
- vld(Visual Leak Detector) 内存泄露检测工具
初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复 杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题.内存 ...
- VS2017 编译 Visual Leak Detector + VLD 使用示例
起因 一个Qt5+VS2017的工程,需要进行串口操作,在自动时发现一段时间软件崩溃了,没有保存log,在 debug 的时候发现每运行一次应用占据的内存就多一点,后来意识到是内存泄漏了.这个真是头疼 ...
- 使用Visual Leak Detector for Visual C++ 捕捉内存泄露
什么是内存泄漏? 内存泄漏(memory leak),指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段 ...
- 使用Visual Leak Detector检测内存泄漏[转]
1.初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题 ...
- Cocos性能优化工具的开发介绍Visual Studio内存泄漏检测工具——Visual Leak Detector
然后,Windows下有什么好的内存泄漏检測工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检測功能.我们能够使用第三方工具Visual Leak Detector(下面简 ...
- Visual Leak Detector原理剖析
认识VLD VLD(Visual Leak Detector)是一款用于Visual C++的开源内存泄漏检测工具,我们只需要在被检测内存泄漏的工程代码里#include “vld.h”就可以开启内存 ...
- 关于Visual Leak Detector的配置与使用 (测试vector 引起的内存泄漏问题)
之前在做一个音频特征提取的批量处理程序,老是出现内存泄露问题,用Visual Leak Detector(VLD)工具做了下检测,检测出了一些问题,解决后还是会有问题.之后继续排查,因为我的代码中,大 ...
- Visual Leak Detector 2.2.3 Visual C++内存检测工具
Visual Leak Detector是一款免费的.健全的.开源的Visual C++内存泄露检测系统.相比Visual C++自带的内存检测机制,Visual Leak Detector可以显 ...
随机推荐
- AX2012 使用HTML自定义popup内样式
在Class Box下新增方法如下: public client static DialogButton yesNoHTML( str _text, DialogButton _defaultButt ...
- Vue中的样式穿透,修改element-ui组件样式不生效
在Vue项目中用的比较多的就是组件,为了实现组件的样式模块化.我们通常会在style标签中添加一个scoped属性,这样css样式只能作用于当前的Vue组件.使组件之间的样式相互独立,当调用该组件的时 ...
- PSO 算法的变体python实现
上演化计算课的时候老师让我们实现EOPSO算法(一种精英反向的粒子群优化算法),下面是他的算法步骤: 首先我们需要知道一些基础知识: (1)基础PSO算法 (2)精英反向解 import numpy ...
- 记录jupyter lab 多个kernel混乱的问题
问题描述 在Anaconda下我有多个虚拟环境,其中一个叫d2l,由于pytorch版本和cuda算力不匹配,重新create了一个环境:d2l_new.然后环境配置好了之后激活环境,启动jupyte ...
- Python pexpect 库的简单使用
一.Python pexpect 库的使用 在终端中许多命令都有与用户交互的场景,例如切换用户时需要手动输入密码,安装应用有时要输入默认配置等.这对 shell 自动化脚本十分不便.expect 命令 ...
- 关于数据传递 json
关于这几种语言的json 操作 Lua local cjson2 = require "cjson" local lua_object = { ["name"] ...
- vite设置跨域
1. vite.config.ts或者vite.config.js文件 server: { port: 3001, host: '0.0.0.0', open: true, proxy: { // 代 ...
- Kicad使用技巧--原理图复用
平时主要使用AD.kicad和立创eda,最近在琢磨一下各个软件的使用技巧,提升一下画图效率,首先想到的就是原理图复用,我想象中的原理图复用应该像AutoCAD的图块功能一样. 这次先说kicad,摸 ...
- __declspec(dllimport) 和 __declspec(dllexport)的使用详解、以及 XX_API 的含义
1. C++代码里调用别人的库.或者写库给别人用.大概有如下的方法(只讨论windows系统的情况): ---- a) 提供头文件 h . 静态库 lib -- > 静态链接 ---- b) ...
- 002基本的Dos命令
002基本的Dos命令 1.开启Dos控制台的几种方式 几种打开CMD的方式: 直接在菜单中搜索"命令提示符". Win+R,输入cmd.(推荐) 在任意文件夹下面,按住Shift ...