说明

使用 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. 微信支付宝app支付回调参数

    微信app支付回调通知参数: <xml><appid><![CDATA[wx9703cd*******]]></appid><attach> ...

  2. 批处理执行指定文件jar包并输出日志

    jar包运行,每次都要手动执行命令.这么机械的操作当然要由脚本来完成. @echo off rem 按当天日期输出日志 set today=%date:~0,4%-%date:~5,2%-%date: ...

  3. server2008R2 安装.net framework 4.7 4.8 时间戳签名和/或证书无法验证或格式错误

    安装补丁 KB4474419 和KB4490628 实测有效 补丁下载: https://www.catalog.update.microsoft.com/Search.aspx?q=4474419 ...

  4. Vue.sync修饰符与this.$emit('update:xxx', newXXX)

    Vue通过prop进行双向数据绑定.子组件数据变化,一般只能通过 this.$emit(func, val) 回调父组件函数来传值给父组件. Vue2.3版本引入sync,作为一个事件绑定语法糖,当子 ...

  5. python基于word模板批量生成word文件

    1.需要用到docxtpl库,用于操作word模板 安装:pip insatll docxtpl 处理之前的word模板 处理后的word 下面直接上代码揭开它的神秘面纱:第一步,读取excel中的内 ...

  6. ICMP-ping报错类型

    ICMP数据包的包头,两个重要字段Type和Code,如图所示 ICMP消息类型和编码类型 回显请求包,正常为80 回显回复包,正常为00 其余均为报错类型. 超时:对方主机不在线.屏蔽等 传输失败: ...

  7. 实验1 Python开发环境使用和编程初体验

    # print输出的几种用法 # 用法1:用于输出单个字符串或单个变量 print('hey, u') # 用法2: 用于输出多个数据项,用逗号分隔 print('hey', ' u') x,y,z ...

  8. EXCEL之VLOOKUP函数——查找、分组、排序

    1,VLOOKUP查找 新建excel,输入测试数据,F列打乱A顺序,查找他们各自对应的学号 选中目标单元格(此处是G2),点击插入函数: 选择VLOOKUP: 我们要查找F列在A列对应的学号,所以G ...

  9. pt-table-check和pt-table-sync实践

    如果主库使用非默认端口,--recursion-method默认值为hosts,这时如果从库没有配置report_host参数(注意该参数缺点),则pt-table-checksum无法自动检测到从库 ...

  10. 【转载】Python 在已存在的excel表格中写入数据

    由于每天都要汇总日报数据,其实就是个复制粘贴的工作,将多个表的数据汇总成一个表格,工作较枯燥,于是想到用Python帮我完成这个简单又粗糙的工作.写一下我学到的几个小技巧,下次忘记怎么使用的时候,还能 ...