A.利用工具umdh(user-mode dump heap)分析:此處以程序MemoryLeak.exe爲例子async
一、開啓cmd函數
鍵入要定位內存泄露的程序gflags.exe /i memroyleak.exe +ust,如圖成功後,開啓memoryleak.exe程序。工具
二、利用UMDH建立Heap快照ui
命令格式:umdh -pn:memoryleak.exe -f:snap1.logspa
程序運行一段時間後或者程序佔用內存增長時,而後再次建立heap快照,命令行無差異,snap1.log改成snap2.log或者其餘。命令行
三、設置好程序的符號路徑,以下圖調試
四、設置好後能夠開始分析heap先後兩個快照的差別日誌
分析差別命令:umdh -d snap1.log snap2.log -f:result.txt,生成的result.txt文件在 命令行同目錄下,這裏是 D:\WinDDK\7600.16385.0\Debuggers code
分析完成後查看結果result.txt,紅色爲umdh定位出來的泄露點,咱們在查看源代碼:server
這樣咱們就能夠修改代碼中內存泄露的地方了。
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時 還未釋放這些內存 因此形成內存比較時 會定位處該塊內存的泄露,至因而否真泄露仍是要看程序邏輯,可是既然已定位到該代碼 仍是要仔細分析一下 看看是邏輯問題 仍是真忘了釋放內存。
開啓memoryleak.exe程序,windbg attach到該進程:
命令:!heap –s查看當前進程運行的全部堆的狀況
而後F5讓程序運行一段時間或者內存有明顯的增長時再次經過!heap –s查看當前堆的變化,以下圖
經過對比先後兩個堆的變化,發現0x012800000該地址的堆增長的很快而其餘堆沒什麼變化,下面進一步定位
命令:!heap –stat –h 查看對應對的狀態,發下該堆的內存基本被長度爲0x424的塊佔用,接下來咱們在堆中搜索該進程中哪些模塊佔用0x424長度內存,以下圖
命令:!heap –flt s 424, 經過搜索程序內存中的堆發現長度爲424的堆被大量的佔用,進一步查看時誰在使用這個地址
找到泄露點了,紅色部分的,若是程序對應的符號對應咱們能夠查看內存泄露點在哪一行