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的堆被大量的占用,进一步查看时谁在使用这个地址
找到泄露点了,红色部分的,若是程序对应的符号对应咱们能够查看内存泄露点在哪一行