轉載自:http://blog.csdn.net/whatday/article/details/47301145
A.利用工具umdh(user-mode dump heap)分析
1設置gflags.exe標誌
r
Gflags標誌設置好後,開啓cmd
鍵入要定位內存泄露的程序gflags.exe /i memroyleak.exe +ust
如圖成功後,開啓memoryleak.exe程序
2利用umdh創建heap快照
命令格式:umdh -pn:memoryleak.exe -f:snap1.log
程序運行一段時間後或者程序佔用內存增加時,然後再次創建heap快照,命令行無差別,snap1.log改爲snap2.log或者其他。
設置好程序的符號路徑,如下圖
設置好後可以開始分析heap前後兩個快照的差異
分析差異命令:umdh -d snap1.log snap2.log -f:result.txt
生成的result.txt文件在 命令行同目錄下 這裏是 D:\WinDDK\7600.16385.0\Debuggers
分析完成後查看結果result.txt
紅色爲umdh定位出來的泄露點,我們在查看源代碼
這樣我們就可以修改代碼中內存泄露的地方了。
內存泄露實例分析:
兩次快照差異文件實例大致如下:
- // 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的堆被大量的佔用,進一步查看時誰在使用這個地址
找到泄露點了,紅色部分的,如果程序對應的符號對應我們可以查看內存泄露點在哪一行
內存泄露分析結束,如果你還有什麼好的方法可以共享