在一次上线过程中iis内存飙升,随后跟运维要到站点的dump文件,使用windbg分析了clr的内存分配,找到了问题的症结,先记录如下:
使用windbg加载dump文件
1.打开windbg,File->Open Crush Dump,打开dump文件;
2.设置符号路径和站点发布文件路径
.sympath C:\MyCodesSymbols*SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols:C:\Users\Administrator\Desktop\symbol
其中,C:\Users\Administrator\Desktop\symbol是pdb文件所在目录,C:\MyCodesSymbols和C:\MyLocalSymbols均为新建文件夹存放符号文件
3.加载sos.dll和clr.dll
.load sos.dll,clr.dll文件所在的目录(可以拷贝这两个dll放在pdb文件目录下)
4.加载pdb文件 .symfix
5.加载.reload
6.输入命令.loadby sos clr
使用windbg命令查看堆内存
1.使用命令!dumpheap -stat 根据内存大小排序查看堆上的对象,查看占用内存比较高的对象和数组等,通过添加 -min 或者 -max 指定最小或者最大size的对象,首次执行这个命令不要添加 -min参数筛选,因为有可能就是一些小的对象比较多引起的内存问题。
2.使用!dumpheap -mt命令查看mt(method table)中的对象地址,上图的第一列为mt,查看指定的MT并显示出Address
!dumpheap -mt 000007fe9a25ebe8
第一列为对象地址(address),可以看到mt的statistic
3.进入这个地址查看具体的对象 !do 000000048a0eb1b0
4.查看对象引用 !gcroot 000000048a0eb1b0
5.根据上图中的线程线程信息定位线程ID,进入线程
!threads命令查看工作的线程,71为线程ID
6.进入71线程:~71s,下图所示已经进入71线程
7.使用!clrstack 命令查看该线程的堆栈信息
上图可以清晰的看到大的对象经过了哪些方法的调用,再结合代码,可以定位出问题。
附录(windbg命令)
内存状态:
!EEHeap -GC 显示托管堆统计信息
!EEHeap -loader 显示加载程序数据结构统计信息!DumpHeap -stat 显示垃托管堆各类型统计信息!DumpHeap -type Free -stat 显示所有碎片类型统计信息!DumpHeap -type System.String -min 150 -max 200 显示所有System.String类型 -min -max 字节统计信息!DumpHeap -min 85000 -stat 显示大对象统计信息!DumpHeap -mt 选项仅列出与指定的 MethodTable 结构对应的那些对象!DumpHeap -mt 00000000022245b0 -min 85000 查看MT 00000000022245b0中大对象!DumpHeap -stat 023e1000 033db630 按地址统计!DumpArray 显示数组对象!DumpObj (!do) 显示有关指定地址处的对象的信息!ObjSize 显示指定对象的大小!DumpStackObjects (!dso) 显示在当前线程内找到的所有托管对象!GCRoot 显示有关对指定地址处的对象的引用(或根)的信息。!CLRUsage 显示托管堆统计信息(GC堆大小,提交内存,虚拟内存),psscor4的扩展命令!DumpMT 显示有关指定地址处的方法表的信息。!DumpMT -MD 显示有关指定地址处的方法表所有方法的列表。!address -summary 显示最大可用区域!vmstat 最大可用区域是 MAXIMUM 列中的最大值线程调用:
!ThreadPool 显示有关托管线程池的信息,包括队列中工作请求的数目、完成端口线程的数目和计时器的数目
!Threads 显示进程中的所有托管线程!Threads -live 选项显示与活动线程关联的线程!Threads -pecial 选项显示由 CLR 创建的所有特殊线程!ThreadState 显示线程的状态。 value 参数为 Threads 报告输出中的 State 字段的值。~54s 转到54线程!CLRStack 提供当前托管代码的堆栈跟踪。!CLRStack -p 选项显示托管函数的参数。!CLRStack -l 选项显示有关帧中的局部变量的信息。!DumpStack 显示堆栈跟踪 包括非托管。 !DumpStack -EE 命令仅显示托管函数。!EEStack 对一个进程中的所有线程运行 DumpStack 命令。k 显示当前线程的call stackkb 显示当前线程的call stack~*kb 显示所有线程的call stack 可以 寻找线程中触发GC的函数(mscorwks!SVR::GCHeap::GarbageCollectGeneration)!ASPXPages 显示当前处理的HttpContext,psscor4的扩展命令!SyncBlk 显示同步块其它:
!runaway 显示线程cpu时间vertarget 查看系统运行时间!PrintException (!pe) 显示在当前线程上引发的最后一个异常!address 命令显示某一地址上的页信息!SaveModule 将加载到内存中指定地址的图像写入指定文件,lm 列出的!SaveModule 081f0000 d:\\commandobject.dll !FinalizeQueue 显示所有已进行终结注册的对象。!GCHandles 显示有关进程中的垃圾回收器句柄的统计信息。S 可以搜索内存 在内存中搜索sina.com: s –u 0012ff40 L?8000000 “sina.com”
r 显示寄存器的信息d 显示内存地址上的值 使用d命令显示esp寄存器指向的内存,默认为byte: d esp用dd命令直接指定054efc14地址,第二个d表示用DWORD格式: dd 054efc14域,程序集,类
!DumpDomain 枚举在指定的 AppDomain 对象地址内加载的每个 Assembly 对象。若在调用 DumpDomain 命令时不提供任何参数,则将列出过程中的所有 AppDomain 对象!DumpAssembly 显示有关程序集的信息。DumpAssembly 命令将列出多个模块(如果存在)。!DumpModule 显示有关指定地址处的模块的信息。 可以使用 DumpDomain 或 DumpAssembly 命令检索模块的地址!DumpModule [-mt] 选项显示模块中定义的类型和模块所引用的类型!FindAppDomain 确定指定地址处的对象的应用程序域!IP2MD <Code address> 显示已 JIT 编译的代码中指定地址处的 MethodDesc 结构。
!DumpMD <MethodDesc address>!U <MethodDesc address> | <Code address> 显示由方法的 MethodDesc 结构指针或方法体内的代码地址指定的托管方法的反汇编(带有批注)