說明
使用 VLD 記憶體泄漏檢測工具輔助開發時整理的學習筆記,
目錄- 說明
- 1. 使用方式
- 2. 有一處記憶體泄漏時的輸出報告(int 型)
- 3. 有一處記憶體泄漏時的輸出報告(int 陣列型)
1. 使用方式
在 QT 中使用 VLD 的方法可以查看另外幾篇博客:
-
【Visual Leak Detector】在 QT 中使用 VLD(方式一)
-
【Visual Leak Detector】在 QT 中使用 VLD(方式二)
-
【Visual Leak Detector】在 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.
輸出與上一節基本類似,這里提幾個不同點:
第 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 這一特殊十六進制數,更詳細的可以查看以下資料,國內可能無法直接訪問:
- Win32 Debug CRT Heap Internals,
- Inside CRT: Debug Heap Management,
- WikiPedia Debug values,
第 24~26 行中,76 bytes 包含有申請 int[10] 的 40 bytes,和管理頭占用的 36 bytes,
本文作者:木三百川
本文鏈接:https://www.cnblogs.com/young520/p/17259449.html
著作權宣告:本文系博主原創文章,著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請附上出處鏈接,遵循 署名-非商業性使用-相同方式共享 4.0 國際版 (CC BY-NC-SA 4.0) 著作權協議,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/548206.html
標籤:其他
