06-windbg定位内存泄露问题

windos调试工具系列

01-windows调试工具(ProcDump使用)

02-windows调试工具(DebugDiag使用)

03-windows分析工具(depends定位动态库加载失败问题)

04-使用windbg调试工具分析-高CPU问题

05-windbg工具定位内存问题-01

概述

之前上线的项目,在服务器的运行过程中发现会有缓慢的内存增长,程序本身没有主动去调用分配内存的API函数,所以初步猜测可能由于使用了第三方库导致了内存泄露,本文简单描述下如何使用windbg来定位windows程序中的内存泄露问题。

一、使用windbg排查内存泄露的步骤

对于一般的内存泄露问题,个人偏向于先在本地模拟测试,看能否重现内存泄露,如果本地总是能复现问题,可以采用代码注释等手段来进行测试,但是对于服务器上缓慢的内存泄露问题,可以根据dump文件或者gflags和umdh程序生成的内存快照进行对比分析,

1.使用dump文件分析

到服务器上创建一个dump文件,使用windbg设置pdb符号文件路径(参考:05-windbg工具定位内存问题),注意:程序的pdb文件一定要与程序对应。我平时在使用jenkins自动编译的时候,都会给对应版本的应用程序打包一个对应时间的pdb文件,

1.使用命令heap -s 显示内存使用情况

在这里插入图片描述
通过显示我们可以观察到,000002a054a20000处堆块占用了大量的内存,

2.!heap -stat -h 000002a054a20000 统计该地址堆内存块使用情况

使用该命令查看,该堆块的内存使用情况,在top 20的内存块中发现,
最多的一个是大小为0x10 字节,分配了0x892f4a次,0x10 * 0x892f4a/1024/1024 大约137M内存,与程序内存泄露的总量差不多,大约猜测在此处进行了内存泄露,
在这里插入图片描述

3.在符号文件中查找对应size的结构体

之前为了测试内存泄露,特意写了一个简单的程序,去pdb文件中查找对应大小的结构体名,0x10 刚好为16个字节,所以去pdb文件中查找大小为16字节的结构体或者类。
如下图所示:服务里面使用了ACE的定时器和另一个第三方的读取点的库(此库是由另一个厂家提供的,之前听他们开发人员提过,该库在高版本的编译器上可能有问题),所以对这两个先表示怀疑,
在这里插入图片描述
难道是ACE的定时器使用的不对?为了验证自己的猜测,加快定时器的频率,在本地进行代码测试,测试并未发现内存泄露,
因为没有读取点库的源代码,所以无法直接进行定位,所以通过此方法暂时无法确认程序大概的内存泄露的地方,这时候需要利用windbgs的gflags和umdh的程序来辅助判断

二.使用windbg的辅助工具定位内存泄露

经过上面简单的分析,暂时无法得出结论,所以需要使用windbgs的安装目录下的gflags.exe和umdh.exe完成从内存检测,以管理员权限运行cmd命令行窗口程序,切换到windbg目录,依次执行下面的命令,

1.设置pdb符号路径

umdh分析出使用堆内存的函数调用堆栈,为方便查看看函数调用堆栈中的具体函数,可以先设置符号的pdb文件路径,设置的变量名称必须为: _NT_SYMBOL_PATH.

#设置符号路径
set  _NT_SYMBOL_PATH="c:\Symbols\;SRV*c:\Symbols\*http://msdl.microsoft.com/download/symbols"

2.调用gflags设置启动umdh的堆栈跟踪

gflags /i   xxx.exe +ust

/i 用于指定进程的名称,参数+ust 给目标进程创建用户堆栈跟踪数据库,用于追踪内存的使用情况

2.第一次使用umdh抓取堆内存快照

umdh.exe -pn:xxx.exe -f:d:\mem1.log

参数-pn用来指定目标进程的进程名,-f参数用来指定存放抓取的堆内存使用快照的数据信息,运行完此命令后,第一次的堆内存快照就保存到d:\mem1.log中

3.等程序运行一段时间,发生内存泄露

在进行第一次的堆内存的快照抓取后,等程序运行一段时间,使程序发生内存泄露,然后再进行第二次抓取堆内存快照。

4.第二次使用umdh抓取堆内存快照

umdh.exe -pn:xxx.exe -f:d:\mem2.log

抓取第二次堆内存使用快照,保存到d:\mem2.log

5.比较两次堆内存快照

umdh.exe d:\mem1.log  d:\mem2.log -f:d:\res.log

比较mem1.log和mem2.log两个文件中堆内存的使用变化量,得出统计数据保存到res.log中,即可查看到堆内存的变化情况,按堆内存的数量从高到低排列,而且有详细的函数的调用堆栈,分析使用量较高的几项即可,
在这里插入图片描述
我是在抓取第一次堆内存快照后,间隔了比较长的时间,抓取了第二次。

  • 23fbeb0 ( 2409090 - d1e0) 240909 allocs BackTrace6A
  • 23fbeb ( 240909 - d1e) BackTrace6A allocations
    格式说明:
    • 23fbeb0 : 表示两次内存快照之间,增加的内存,
    • 2409090:第二次使用内存快照时该堆占用内存,
    • d1e0:第一次使用内存快照时该堆占用的内存
    • 240909:总的分配次数
    • 23fbeb:两次之间,多分配的次数
    • 240909:第二次内存快照到第一次之间分配次数
    • d1e:第一次的内存快照分配的次数
      0x23fbeb0/0x23fbeb = 0x10 刚好16个字节,符合我们前面分析的内存泄露的字节大小,
      从调用堆栈上看,内存泄露的地方像是在使用标准的std::string 中发生的,根据调用堆栈提示的代码行数,找到对应的代码部分。
      在这里插入图片描述
      代码的655行是对一个string类型的值进行赋值,查找该类型所属的结构体,发现了问题,以前在进行结构体数据清0的时候,使用了memset()函数对整个结构体清0,但是结构体中后来增加了std::string 类型,memset会把std::string 内存维护的指针清空,所以等在给std::string赋值的时候,又需要重新分配内存,所以该处导致了内存泄露,所以即使程序里面没有动态申请内存,这个地方也是导致了内存泄露,
      在这里插入图片描述

总结

1.注意事项

对于含有string类型的结构体对象,不能使用了memcpy、memmove、memset函数操作其内存,否则会导致一些程序的异常和内存泄露问题,本程序中的std::string 使用的字符串都比较小,小于std::string 的预设的16个字节,所以每声明一次该对象,就发生一次内存泄露。

2.为了简化生成堆内存的快照步骤,使用脚本

如果所示意:为了简化操作步骤,将windbg程序打包,写了脚本命令,按照顺序使用管理员权限运行1,2,3,4脚本命令,在set_NT_SYMBOL_PATH.bat中修改保存路径和要检测的程序名称和pdb符号路径
在这里插入图片描述
set_NT_SYMBOL_PATH.bat

rem 设置pdb文件路径和要捕捉的进程的名称  并设置输出文件
set   currsymbolpath=%~dp0
rem 设置符号路径 根据实际需求设置
set  _NT_SYMBOL_PATH="c:\Symbols\"
set  _NT_SYMBOL_PATH=%_NT_SYMBOL_PATH%;%currsymbolpath%
rem 设置windb到环境变量
set  PATH=%PATH%;%currsymbolpath%\windbg_x64
rem 设置要捕捉的进程的名称
set  PROCESS=mem.exe
rem  设置输出的文件
set  OUT1=%currsymbolpath%\mem1.log
set  OUT2=%currsymbolpath%\men2.log
set  OUTRES=%currsymbolpath%\res.log
echo %_NT_SYMBOL_PATH%

1.gflags.bat

set   CURRDIR=%~dp0
call  %CURRDIR%\set_NT_SYMBOL_PATH.bat
gflags  -i  %PROCESS% +ust
pause

2.umdh.bat

set   CURRDIR=%~dp0
call  %CURRDIR%\set_NT_SYMBOL_PATH.bat
umdh.exe   -pn:%PROCESS%   -f:%OUT1%
pause

3.umdh.bat

set   CURRDIR=%~dp0
call  %CURRDIR%\set_NT_SYMBOL_PATH.bat
umdh.exe   -pn:%PROCESS%   -f:%OUT2%
pause

4.res.bat

set   CURRDIR=%~dp0
call  %CURRDIR%\set_NT_SYMBOL_PATH.bat
umdh.exe  %OUT1%  %OUT2%    -f:%OUTRES%
pause

3.资源下载

windbg下载内置内存泄露命令脚本

猜你喜欢

转载自blog.csdn.net/qq_37103755/article/details/128399763
今日推荐