A.利用工具umdh(user-mode dump heap)分析:此處以程序MemoryLeak.exe為例子
1、開啟cmd
鍵入要定位內存泄露的程序gflags.exe /i memroyleak.exe +ust,如圖成功后,開啟memoryleak.exe程序。
2、利用UMDH創建Heap快照

命令格式:umdh -pn:memoryleak.exe -f:snap1.log
程序運行一段時間后或者程序占用內存增加時,然后再次創建heap快照,命令行無差別,snap1.log改為snap2.log或者其他。
3、設置好程序的符號路徑,如下圖
4、設置好后可以開始分析heap前后兩個快照的差異
分析差異命令:umdh -d snap1.log snap2.log -f:result.txt,生成的result.txt文件在 命令行同目錄下,這里是 D:\WinDDK\7600.16385.0\Debuggers
分析完成后查看結果result.txt,紅色為umdh定位出來的泄露點,我們在查看源代碼:
這樣我們就可以修改代碼中內存泄露的地方了。
B、內存泄露實例分析
兩次快照差異文件實例大致如下:
// Debug library initialized ... D0000-111FFF DBGHELP: Server - private symbols & lines C:\FunctionServer\Release\Server.pdb 77E70000-77FEFFFF DBGHELP: ntdll - public symbols c:\mysymbol\wntdll.pdb\B081677DFC724CC4AC53992627BEEA242\wntdll.pdb 。。。。 等等符號加載信息 緊接着是內存泄露信息格式說明 // // Each log entry has the following syntax: // // + BYTES_DELTA (NEW_BYTES - OLD_BYTES) NEW_COUNT allocs BackTrace TRACEID // + COUNT_DELTA (NEW_COUNT - OLD_COUNT) BackTrace TRACEID allocations // ... stack trace ... // // where: // // BYTES_DELTA - increase in bytes between before and after log // NEW_BYTES - bytes in after log // OLD_BYTES - bytes in before log // COUNT_DELTA - increase in allocations between before and after log // NEW_COUNT - number of allocations in after log // OLD_COUNT - number of allocations in before log // TRACEID - decimal index of the stack trace in the trace database // (can be used to search for allocation instances in the original // UMDH logs). // 接着是具體的內存泄露信息 + 47e0 ( 237238 - 232a58) 1f9 allocs BackTrace8E5CFAC + 4 ( 1f9 - 1f5) BackTrace8E5CFAC allocations ntdll!RtlAllocateHeap+274 Server!malloc+49 (f:\dd\vctools\crt\crtw32\heap\malloc.c, 92) Server!operator new+1D (f:\dd\vctools\crt\crtw32\heap\new.cpp, 59) Server!CUi::AddItemText+129 (d:\projects\testtest\common\uilibf, 611) Server!CUi::AddItemInt+57 (d:\projects\testtest\common\uilibf, 709) Server!CMainWin::AddOneFunction+1FE (d:\projects\testtest\server\server\, 361) Server!CTest::FunctionPcInfo+3F9 (d:\projects\testtest\server\server\, 306) Server!CTest::FunctionReadDispatch+15D (d:\projects\testtest\server\server\, 105) Server!CTest::FunctionReadCallback+14 (d:\projects\testtest\server\server\, 76) Server!CWSAAsync::ReadProc+10F (d:\projects\testtest\common\wsaasyncselect, 1336) Server!CWSAAsync::ReadProcMiddle+12 (d:\projects\testtest\common\wsaasyncselect, 1296) Server!CWindowsPool::ReadThreadPoolCallback+25 (d:\projects\testtest\common\wsaasyncselect, 332) ntdll!TppWorkpExecuteCallback+10F ntdll!TppWorkerThread+572 kernel32!BaseThreadInitThunk+E ntdll!__RtlUserThreadStart+70 ntdll!_RtlUserThreadStart+1B 。。。。 等等其他內存泄露塊信息
根據格式的說明可得到此泄露信息如下:
第一行:+ 47e0 ( 237238 - 232a58) 1f9 allocs BackTrace8E5CFAC
BackTrace8E5CFAC是這個內存塊的標記 237238是生成日志文件2時該內存塊的大小 232a58是生成日志文件1該內存塊的大小 差值47e0 是內存泄露的字節數 1f9是分配內存的次數 (其中47e0 個人理解為申請內存未釋放的字節數,因為有可能是釋放的時間未到就生成日志文件2 造成只有申請內存 沒有釋放的情況 所以被判定為內存泄露 關於這點只是個人意見 不一定正確) 。
第二行:+ 4 ( 1f9 - 1f5) BackTrace8E5CFAC allocations
BackTrace8E5CFAC是內存塊標記和第一行一樣,1f9是生成日志文件2時該內存分配的次數, 1f5是生成日志文件1時該內存分配的次數 差值4是這次該內存塊分配的次數。
其他行:是函數調用堆棧,通過分析自己的程序發現,第三行的 Server!CUi::AddItemText+129 (d:\projects\testtest\common\uilibf, 611) 也是內存泄露所在,對應源代碼是:pItemLabel = new CLabelUI; 這樣基本上就定位到問題所在了
驗證一下觀點:每一次分配的大小是47e0 /4=4600(十進制), 程序中代碼驗證了sizeof(CLabelUI)也等於4600, 看來從日志1 到日志2 過程中這個地方new了4次 但是在日志2時 還未釋放這些內存 所以造成內存比較時 會定位處該塊內存的泄露,至於是否真泄露還是要看程序邏輯,但是既然已定位到該代碼 還是要仔細分析一下 看看是邏輯問題 還是真忘了釋放內存。
B.Windbg手動分析內存泄露
1、全局標志設置,參照上邊的設置
2、Windbg調試泄露
開啟memoryleak.exe程序,windbg attach到該進程:
命令:!heap –s查看當前進程運行的所有堆的情況
然后F5讓程序運行一段時間或者內存有明顯的增加時再次通過!heap –s查看當前堆的變化,如下圖
通過對比前后兩個堆的變化,發現0x012800000該地址的堆增加的很快而其他堆沒什么變化,下面進一步定位
命令:!heap –stat –h 查看對應對的狀態,發下該堆的內存基本被長度為0x424的塊占用,接下來我們在堆中搜索該進程中哪些模塊占用0x424長度內存,如下圖
命令:!heap –flt s 424, 通過搜索程序內存中的堆發現長度為424的堆被大量的占用,進一步查看時誰在使用這個地址
找到泄露點了,紅色部分的,如果程序對應的符號對應我們可以查看內存泄露點在哪一行