说明
使用 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. 有三处内存泄漏时的输出报告
写一个有三处内存泄漏的程序,如下:
#include <QCoreApplication>
#include "vld.h"
void testFun1()
{
int *ptr = new int(0x55345678);
printf("ptr1 = %08x, *ptr1 = %08x.\n", ptr, *ptr);
}
void testFun2()
{
short *ptr = new short(0x4529);
printf("ptr2 = %08x, *ptr2 = %04x.\n", ptr, *ptr);
}
void testFun3()
{
char *ptr = new char[3];
printf("ptr3 = %08x.\n", ptr, *ptr);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
testFun1();
testFun2();
testFun3();
return a.exec();
}
程序运行时,在标准输出窗会输出以下结果:
ptr1 = 00b674b8, *ptr1 = 55345678.
ptr2 = 00b670f8, *ptr2 = 4529.
ptr3 = 00b674e8.
程序运行结束后,检测到了内存泄漏,VLD 会输出以下报告(本例中出现三处内存泄漏),第 1~3 行显示 VLD 运行状态,第 4~19 行显示 testFun2()
函数泄漏内存的详细信息,第 22~37 行显示 testFun1()
函数泄漏内存的详细信息,第 40~55 行显示 testFun3()
函数泄漏内存的详细信息,第 58~60 行总结此次泄漏情况,第 61 行显示 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 2 at 0x00B670F8: 2 bytes ----------
Leak Hash: 0xB9FC7D06, Count: 1, Total 2 bytes
Call Stack (TID 14904):
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 (12): testVLD.exe!testFun2() + 0x7 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (28): 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:
29 45 )E...... ........
---------- Block 1 at 0x00B674B8: 4 bytes ----------
Leak Hash: 0xE622CBED, Count: 1, Total 4 bytes
Call Stack (TID 14904):
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!testFun1() + 0x7 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (27): 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.... ........
---------- Block 3 at 0x00B674E8: 3 bytes ----------
Leak Hash: 0x1ED7DC7D, Count: 1, Total 3 bytes
Call Stack (TID 14904):
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 (18): testVLD.exe!testFun3() + 0x7 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (30): 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:
CD CD CD ........ ........
Visual Leak Detector detected 3 memory leaks (117 bytes).
Largest number used: 117 bytes.
Total allocations: 117 bytes.
Visual Leak Detector is now exiting.
观察以上输出报告,可以总结出以下规律:
Block
后面的序号和函数调用顺序有关,先调用的序号小,在主函数中testFun1()
、testFun2()
、testFun3()
是依次调用的,分别对应于Block 1
、Block 2
、Block 3
。- 报告输出时泄漏信息的显示顺序和地址大小有关,地址小的先显示,
Block 1
、Block 2
、Block 3
的泄漏首地址分别为0x00B674B8
、0x00B670F8
、0x00B674E8
,内存地址从小到大排序为Block 2
、Block 1
、Block 3
,显示顺序正是如此。
每个 Block
输出的详细解析可以查看另外一篇博客 【Visual Leak Detector】QT 中 VLD 输出解析(二)。第 58~60 行中的 117 bytes
包含有: Block 1
中申请 int
的 4 bytes
及对应的 36 bytes
内存管理头、 Block 2
中申请 short
的 2 bytes
及对应的 36 bytes
内存管理头、 Block 3
中申请 char[3]
的 3 bytes
及对应的 36 bytes
内存管理头,共计 \(4 + 36 + 2 + 36 + 3 + 36 = 117 bytes\)。
3. 有两处内存泄漏时的输出报告
将 testFun2()
函数中申请的内存正常释放,测试代码如下:
#include <QCoreApplication>
#include "vld.h"
void testFun1()
{
int *ptr = new int(0x55345678);
printf("ptr1 = %08x, *ptr1 = %08x.\n", ptr, *ptr);
}
void testFun2()
{
short *ptr = new short(0x4529);
printf("ptr2 = %08x, *ptr2 = %04x.\n", ptr, *ptr);
delete ptr;
}
void testFun3()
{
char *ptr = new char[3];
printf("ptr3 = %08x.\n", ptr, *ptr);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
testFun1();
testFun2();
testFun3();
return a.exec();
}
程序运行时,在标准输出窗会输出以下结果:
ptr1 = 006c5c68, *ptr1 = 55345678.
ptr2 = 006c5cc8, *ptr2 = 4529.
ptr3 = 006c5db8.
程序运行结束后,检测到了内存泄漏,VLD 会输出以下报告(本例中出现两处内存泄漏),第 1~3 行显示 VLD 运行状态,第 4~19 行显示 testFun1()
函数泄漏内存的详细信息,第 22~37 行显示 testFun3()
函数泄漏内存的详细信息,第 40~42 行总结此次泄漏情况,第 43 行显示 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 0x006C5C68: 4 bytes ----------
Leak Hash: 0x1E4EE072, Count: 1, Total 4 bytes
Call Stack (TID 11992):
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!testFun1() + 0x7 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (28): 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.... ........
---------- Block 3 at 0x006C5DB8: 3 bytes ----------
Leak Hash: 0xE91F4A96, Count: 1, Total 3 bytes
Call Stack (TID 11992):
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 (19): testVLD.exe!testFun3() + 0x7 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (31): 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:
CD CD CD ........ ........
Visual Leak Detector detected 2 memory leaks (79 bytes).
Largest number used: 79 bytes.
Total allocations: 117 bytes.
Visual Leak Detector is now exiting.
观察以上输出报告,可知 Block
后面的序号不仅仅针对导致内存泄漏的函数,这个例子中的 Block 2
应该属于 testFun2()
,但由于 testFun2()
正常释放了内存,因此在输出报告中没有找到 Block 2
,只能找到 Block 1
和 Block 3
,这个例子说明了输出报告中 Block
序号不一定是连续的,可能缺失了一些序号,属正常现象。第 40~41 行中的 79 bytes
包含有: Block 1
中申请 int
的 4 bytes
及对应的 36 bytes
内存管理头、 Block 3
中申请 char[3]
的 3 bytes
及对应的 36 bytes
内存管理头,共计 \(4 + 36 + 3 + 36 = 79 bytes\)。第 42 行中的 117 bytes
表示运行过程中一共分配的内存大小,计算方式与上一节一样。
- Detector Visual Leak VLDdetector visual leak vld detector方式visual leak skipheapfreeleaks detector visual leak 源码detector visual leak detector visual leak 2015 skipcrtstartupleaks detector visual leak forceincludemodulesmd detector visual leak stackwalkmethod detector visual leak detector版本visual leak traceinternalframes detector visual leak