iles (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 的 Debug
编译器时,这块管理内存的大小为 36bytes
,当使用 64 bit 的 Debug
编译器时,大小变为 52bytes
。差别产生的原因是 _CrtMemBlockHeader
结构体中含有指针类型成员,而指针类型在不同位数的环境下内存量宽度是不一样的。)
第 23 行,表示整个检测过程中全部泄漏块总大小的最大值(即检测过程中 \(max{\{上一行括号内的值\}}\)),为 40 bytes
,原因看前一条,有兴趣的可以阅读 VLD
源码。
第 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:\cw