用WinDbg排除“内存溢出”故障

 

用WinDbg排除“内存溢出”故障

 

文章摘要

内存溢出有时像“魔鬼”一样缠绕着我们的程序,用一般的方法不易驱除。主要难点是搜查“魔鬼”的藏身之处。这时,我们可以请来WinDbgDebugging Tools for Windows)这个“钟馗”来“找一找,抓一抓”。利用WinDbg工具包(包括与之配套的sos.dll成员等)丰富的命令可以用多种方式查看非常之细的内存块内容。以助我们找到问题的“元凶”。

关键词

内存溢出 WinDbg 跟踪调试

一、前言

对程序进行“内存溢出”的故障诊断时,可采取两种方法:(一)在程序运行期间,以hang 的方式附加到进程序上,在线分析。但这个方式将致使进程暂停,所以作为生产环境,时间不可能太长。可替代的方式是Dump几个内存映象文件,然后作对比分析。(二)“内存溢出”一般最终会导致程序的DOWN掉。但其它许多原因也会有这个结果。所以,为了确认DOWN掉的原因,这第二种方法就是将WinDbgcrash的方式附加到运行的进程上,这不会使进程暂停,直到它被KILL掉。然后分析其产生的Dump文件。近段时间,对某局点EasyB接口机“内存溢出”的故障跟踪分析就是按这两种方式进行的,对其抓取的Dump文件初步分析,基本确定了“元凶”的大致范围,下一步将结合代码进行故障的排除。由于在公司内部无法复现其DOWN掉的情形,因此在现场将WinDbg附加到了其进程上,如果其DOWN掉,我们就会对其原因更加确切。

二、正文

(一)WinDbg初始化

1.下载最新的Windbg,安装在被测机上。
2.打开Windbg,在File->Symbol File Path 窗口中输入

 C:\WINDOWS\Symbols;d:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\symbols;.sympath SRV*d:\localsymbols*http://msdl.microsoft.com/download/symbols

否则可能会出现如下错误信息:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\ntdll.dll
当然这时,输入 .load C:\WINDOWS\system32\ntdll.dll 命令也可解决。
3.运行需要调试的程序,在Windbg中File->Attach to Process中选择刚才运行的程序。如果是分析Dump文件则打开相应文件。
4.在出现的Command窗口中输入命令

.load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll

否则可能会出现如下错误信息:
No export dumpobj found
好,一切准备就绪,下面正式开始“抓鬼” 。

(二)用WinDbg hang模式,查找“内存溢出”的元凶

一般进行以下四个步骤:

1!dumpheap –stat查看此进程所有对象的数量及所占内存大小;

2、试着筛选出某类比较多的对象,用!dumpheap -mt (地址)去找找几个对象的地址;

3、用!do (地址)命令查看对象的状态,属性的值等,看看能否发现一些端倪;

4、用!gcroot -nostacks (地址)查看对象的根正常不正常,如果有些对象的根不是自己预先设计的那样,很可能被自己没想到的对象强引用了,所以GC无法回收它,就泄漏了。

如果分析Dump文件,那之前Dump文件时用以下相似命令:

adplus.vbs -hang -p XXXX.exe-o d:"dump

下面预览一下运行这几个命令的结果,并作简要分析

0:007> !dumpheap -stat

此命令结果是按TotalSize(对象内存占用大小)排序的

total 11376 objects

Statistics:

      MT    Count    TotalSize Class Name

7a79375c        1           12 System.Diagnostics.EventLogEntryType

7a78cd28        1           12 System.Net.Configuration.ProxyElement+AutoDetectValues

00db6aa0        1           12 Zte.EasyB.Common.KeyLockObj

00db5210        1           12 Zte.EasyB.Common.AlarmAgentConfigurationSectionHandler

00db50d8        1           12 Zte.EasyB.Common.NullAlarmAgent

……

7910234c      389         9336 System.Collections.ArrayList

790fd8b4      167         9352 System.Collections.Hashtable

79122610      170        29496 System.Collections.Hashtable+bucket[]

7912254c      851        54848 System.Object[]

00154c50       13       368000      Free

790f9244     6218       374644 System.String

Total 11376 objects

从这个结果可初步查看哪些对象太多,占用内存过大。如结果中的:System.Collections.ArrayList System.Object[]。当然,System.String最多,不过从经验看,每个程序这个对象都是最多的。从一个侧面,这告戒我们,在进行字符串连接运算时,最好不要用 String + String ……,而要习惯地用StringBuilder对象。有兴趣的同事可以自己用代码测测。如果要测试的话,还告诉你一个命令:!dumpheap -mt 790f9244-strings,会看得更清楚。

0:007> !dumpheap -mt 7910234c

Address       MT     Size

011412d0 7910234c       24    

01141334 7910234c       24    

01141630 7910234c       24    

01141fb0 7910234c       24   

……

0117f834 7910234c       24    

total 389 objects

Statistics:

      MT    Count    TotalSize Class Name

7910234c      389         9336 System.Collections.ArrayList

Total 389 objects

这就可以查看每个对象的“老家”了,当然这些对象中大部份是“良民”,要辩别出谁是“魔鬼”,还得用下面的手段。

0:007> !do 011412d0

Name: System.Collections.ArrayList

MethodTable: 7910234c

EEClass: 791022a0

Size: 24(0x18) bytes

 (C:"WINDOWS"assembly"GAC_32"mscorlib"2.0.0.0__b77a5c561934e089"mscorlib.dll)

Fields:

MT       Field       Offset      Type VT       Attr    Value Name

7912254c 40008c6        4      System.Object[] 0 instance 011412e8 _items

790fdb60 40008c7        c      System.Int32     0 instance        2 _size

790fdb60 40008c8       10      System.Int32     0 instance        2 _version

790f8a7c 40008c9        8       System.Object   0 instance 00000000 _syncRoot

7912254c 40008ca      1b4      System.Object[]   0   shared   static emptyArray

    >> Domain:Value 0014e8d8:01131dec <<

可以看到这对象是一个ArrayList,其大小为2,具体的值保存在地址011412e8中,是一个System.Object[]类型的数组

这儿插一下,因为我今天运气好,选到一个数组对象作为例子。所以,我们就顺势再介绍两个步骤:以查看数据组中元素的值。

 * 查看数组信息

  0:007> !DumpArray 011412e8                                                         

 Name: System.Object[]

 MethodTable: 7912ad90

 EEClass: 7912b304

 Size: 32(0x20) bytes

 Array: Rank 1, Number of elements 4, Type CLASS

 Element Methodtable: 790fc35c

 [0] 012a1b50

 [1] 012a1b6c

 [2] null

 [3] null

* 查看数组内第一个元素的值

  0:007> !do 012a1b50                                                                  

 Name: System.String

 MethodTable: 790fcb30

 EEClass: 790fca90

 Size: 26(0x1a) bytes

   (C:"WINDOWS"assembly"GAC_32"mscorlib"2.0.0.0__b77a5c561934e089"mscorlib.dll)

 String: Soap

 Fields:

   MT Field Offset Type VT Attr Value Name

 791018e0 4000096 4 System.Int32 1 instance 5 m_arrayLength

 791018e0 4000097 8 System.Int32 1 instance 4 m_stringLength

 790fe534 4000098 c System.Char 1 instance 61 m_firstChar

 790fcb30 4000099 10 System.String 0 shared static Empty

   >> Domain:Value 00149c58:790d81bc <<

 7912b1d8 400009a 14 System.Char[] 0 shared static WhitespaceChars

   >> Domain:Value 00149c58:012a16f0 << 

那个Soap 就是值了

0:007> !gcroot -nostacks 011412e8

DOMAIN(0014E8D8):HANDLE(Pinned):7d13f0:Root:02133030(System.Object[])->

011412c4(System.Configuration.ConfigurationPropertyCollection)->

011412d0(System.Collections.ArrayList)->

011412e8(System.Object[])

好,通过以上步骤,再加上你仔细的分析,应该会让“魔鬼”现原型的。

(三)用WinDbg crash模式,查找“内存溢出”的元凶

Windows命令窗口中操作,如下两步就行:

1.进入命令行(CMD),并转到WinDBG安装目录(默认是c:"program files"debugging tools for windows), 运行以下这命令

cscript adplus.vbs -pn XXX.exe -crash -quiet -o <c:"dump>

2.让该进程继续工作,等问题发生后,会在c:"dumps下面产生几个目录,确定里面有(.dmp)文件

由于等现场DOWN可能要两三个月,所以我用另外的进程作了个测试,看看到底会产生什么样的文件内容。我在“任务管理器”中将一个进程关闭了,以下为关键内容。

--- 1st chance Process_Shut_Down exception ----

This process is shutting down!

This can happen for the following reasons

1) Someone killed the process with Task Manager or the kill command

2.) If this process is an MTS or COM+ server package, it could be

*    exiting because an MTS/COM+ server package idle limit was reached.

3.) If this process is an MTS or COM+ server package,

*    someone may have shutdown the package via the MTS Explorer or

*    Component Services MMC snap-in.

4.) If this process is an MTS or COM+ server package,

*    MTS or COM+ could be shutting down the process because an internal

*    error was detected in the process (MTS/COM+ fail fast condition).

很明显,加粗部份就给我们显示了其被Kill的原因。

三、总结

本文就排除“内存溢出”的问题,简单谈了谈WinDbg的用法,由于是初次使用WinDbg,其中必然有错误或遗漏的地方,望大家指出。另外,WinDbg还有很多命令,可解决的故障也很多,如线程池耗尽,线程死锁等等。大家可对自己所遇到的问题进行针对地分析。

原文地址:https://www.cnblogs.com/xzp/p/1183340.html