图片 1

Windbg使用简明指南,Redirect引起的属性难题浅析

Posted by

在前面的文章里面,执行性能测试—起步里,讲了执行性能测试的基本步骤,而且在前面的例子里面,通过一个2M多的文本文件,对比了冒泡排序和快速排序的性能之间的差别。但是当我使用一个700M大小的文本文件进行测试的时候—因为我需要了解在极端情况下两种排序方法的差别。原定是2G的文本文件,但是无论快排还是冒泡排序都要求被排序的数据完全存在于内存当中,对于32位机,2G的数据是一个上限,因为操作系统的内核代码使用掉了另外2G的地址空间—除非你使用/LARGEADDRESSAWARE这个开关,限制操作系统只使用1G的内存,而让用户态代码使用3G的空间。

第一章 准备

现象:

 

1.1.    环境配置

 

_NT_DEBUGGER_EXTENSION_PATH=C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727

_NT_SYMBOL_PATH=SRV*c:\Symbols*

 

Path add:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727

C:\Program Files\Debugging Tools for Windows (x86)

 

最近做的一个系统通过单点登录(SSO) 技术验证用户登录。用户在SSO
系统上通过验证后,跳转到该系统的不同模块。而跳转的时间一直维持子啊几分钟左右。

为了重现这个问题,我们先来准备一下数据,用下面两个DOS命令就可以准备好这些数据了:

1.2 .Net CLR知识

图片 1

                       

分析步骤:

 

第二章 常用命令

在问题复现时抓取Hang dump 进行分析:

1.   dir /s /b c:\windows >
d:\test.txt

2.1 基本命令

序号

命令

解释

  1.    

.chain

显示有哪些调试扩展。

  1.    

.load DLLName

!DLLName.load

加载调试扩展。DLLName要是全路径名,包括”.dll”

  1.    

.loadby DLLName ModuleName

加载调试扩展。DLLName是短文件名,不包括”.dll”。

ModuleName是调试进程中的模块名,表示通过它所在的路径查找DLLName。

  1.    

.unload DLLName

!DLLName.unload

卸载调试扩展。

  1.    

.setdll DLLName

!DLLName.setdll

设置缺省的调试扩展。

  1.    

![ext.]address

显示VM的分配状况

  1.    

![sos.]vmmap

显示VM的分配状况

  1.    

![uext.]vadump

输出虚拟地址映射信息

  1.    

![uext.]vprot address

显示给出的虚拟地址所在内存的映射信息。

  1.  

.logfle

检查是否有日志文件

  1.  

.logopen

.logappend

打开日志文件。输出内容多时特别有用!

一个是新建,另一个是追加。

  1.  

.logclose

关闭日志文件

  1.  

dt

通过符号输出类型的结构拓扑信息。

  1.  

![ntsdexts.]heap 0 0

察看Win32堆的运行状况。

使用!heap –stat察看heap的内存使用情况。

  1.  

![ext.]dlls

可以察看进程内的动态链接库的信息。

  1.  

![sos.]threads

可以列出所有的托管线程,并在栈顶给出异常对象的地址。

  1.  

![sos.]ip2md addr

可以将IP地址转换成IL对应的方法名。

  1.  

![sos.]BPMD <module name>   <method name>

![sos.]BPMD -md <MethodDesc>

可以在托管代码的指定位置设置断点。

method name是包含namespace.class.method的全名!

!bpmd myapp.exe MyApp.Main

Example for generics:

      Given   the following two classes:

      class   G3<T1, T2, T3>

      {

      …

      public void F(T1 p1, T2 p2, T3 p3)

      { … }

      }

      public   class G1<T> {

          // static method

          static public void G<W>(W w)

          { … }

      }

 

      One   would issue the following commands to set breapoints on G3.F() and  G1.G():

 

      !bpmd   myapp.exe G3`3.F

      !bpmd   myapp.exe G1`1.G

  1.  

![sos.]Name2EE module_name item_name

![sos.]Name2EE module_name!item_name

查找名称(类型/方法/属性)对应的CLR信息

Examples:

 !Name2EE    mscorlib.dll System.String.ToString

 !Name2EE *!System.String

Examples:

0:018> !name2ee mscorlib.dll   System.IntPtr

Module: 790c2000 (mscorlib.dll)

Token: 0x020000c1

MethodTable: 790fe160

EEClass: 790fe0d0

Name: System.IntPtr

  1.  

![sos.]dumpclass

查找EEClass对应的信息

Examples:

0:018> !DumpClass 790fe0d0

Class Name: System.IntPtr

mdToken: 020000c1   (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

Parent Class: 790f9d24

Module: 790c2000

Method Table: 790fe160

Vtable Slots: 5

Total Method Slots: 1b

Class Attributes: 102109 

NumInstanceFields: 1

NumStaticFields: 1

        MT    Field   Offset                 Type VT     Attr      Value Name

79118260  40003e0        4                  PTR  0 instance           m_value

790fe160  40003e1        96c        System.IntPtr  0     shared   static Zero

      >> Domain:Value    000da220:NotInit  000fde60:0   <<

  1.  

![sos.]token2ee

查找mdToken对应的名称信息。

Examples:

0:018> !Token2EE mscorlib.dll 020000c1

Module: 790c2000 (mscorlib.dll)

Token: 0x020000c1

MethodTable: 790fe160

EEClass: 790fe0d0

Name: System.IntPtr

  1.  

c rgBuf1 L 100 rgBuf2

比较内存

  1.  

as

设置别名,例:

as Name EquivalentLine

aS Name EquivalentPhrase

aS Name "EquivalentPhrase"

as /e Name EnvironmentVariable

as /ma Name Address

as /mu Name Address

as /msa Name Address

as /msu Name Address

as /x Name Expression

aS /f Name File

as /c Name CommandString

  1.  

ad

删除别名,例:

ad [/q] Name

ad *

  1.  

al

列表别名。例:

al

  1.  

${ }

解释(提取)别名,例:

Text ${Alias} Text

Text ${/d:Alias} Text

Text ${/f:Alias} Text

Text ${/n:Alias} Text

Text ${/v:Alias} Text

  1.  

特别有用的MASM命令处理函数.

$fnsucc(FnAddress, RetVal, Flag)

$iment (Address)

$scmp("String1",   "String2")

$sicmp("String1",   "String2")

$spat("String",   "Pattern")

$vvalid(Address, Length)

  1.  

~#s

设置当前线程上下文。例:

0: kd> ~1s

1: kd>

  1.  

$<, $><, $$<, $$><, $$>a<

(Run Script File)

$<Filename

$><Filename

$$< Filename

$$>< Filename

$$>a< Filename arg1   arg2 arg3 … argn

 

  1.  

!runaway

The   !runaway extension displays information about the time consumed by each   thread.

查看线程信息

Flags

      Specifies the kind of information to be displayed. Flags can be any   combination of the following bits. The default value is 0x1:

Bit 0 (0x1)

      Causes the debugger to show the amount of user time consumed by each   thread.

Bit 1 (0x2)

      Causes the debugger to show the amount of kernel time consumed by each   thread.

Bit 2 (0x4)

      Causes the debugger to show the amount of time that has elapsed since   each thread was created.

0:001> !runaway 7

例:

 User Mode Time

 Thread         Time

 0:55c        0:00:00.0093

 1:1a4        0:00:00.0000

 

 Kernel Mode Time

 Thread         Time

 0:55c        0:00:00.0140

 1:1a4        0:00:00.0000

 

 Elapsed Time

 Thread         Time

 0:55c        0:00:43.0533

 1:1a4        0:00:25.0876

  1.  

!envvar Variable

获取环境变量的值

0:000>   !envvar _nt_symbol_path

        _nt_symbol_path =   srv*C:\mysyms*http://msdl.microsoft.com/download/symbols

  1.  

.printf

类似C库的printf函数,可以接收的参数有:

%p, %N, %I, %ma, %mu, %msa, %msu, %y, %ly

除了文档中给出的参数,还可以接收C库中的格式化字符串,如:

%d, %c, %d, %f, %E等,功能强劲。

如果需要输出64位数字,可以使用"%I64x"。如:

0:000> .printf "%I64x",   0x00001234`00000123;

123400000123

  1.  

n [Radix]

Set   Number Base

命令格式:

    n [Radix]

Radix  

    Specifies the default number base that is   used for numeric display and entry. You can use one of the following values.

    8      Octal   

    10     Decimal

    16     Hexadecimal

  1.  

![sos.]clrstack

!CLRStack [-a] [-l] [-p]

-a –l –p选项可以输出栈的参数,非常有用!

  1.  

![sos.]DumpStack

!DumpStack [-EE] [top stack [bottom stack]]

比ClrStack的输出内容更为详尽,能同时显示native和.NET的堆栈信息!top stack/bottom stack是从k系列命令看到的栈顶和栈底地址。 -EE 限制只输出.NET堆栈。

如:

!DumpStack 77b1dae4 77b1dba4

  1.  

![exts.]tp

!tp   pool Address [Flags]

!tp   tqueue Address [Flags]

!tp   ItemType Address [Flags]

!tp   ThreadType [Address]

!tp   -?

察看OS的thread pool信息

  1.  

![sos.]threadpool

察看.NET使用的thread pool的统计信息。

  1.  

x

x [Options] Module!Symbol

x [Options] *!*

检查加载的符号,特别有用!

注意,Module是模块名,不带.dll。例:

0:000> x mymodule!*spin*

0:000> x *!*

  1.  

j

j   (Execute If – Else)

j   Expression Command1 ; Command2

j   Expression ‘Command1’ ; ‘Command2’

If   this expression evaluates to a nonzero value, Command1 is executed. If this   expression evaluates to zero, Command2 is executed.例:

0:000> bp `mysource.cpp:143` "j   (poi(MyVar)>0n20) ”; ‘gc’ "

0:000> j (MySymbol=0) ‘r eax’; ‘r ebx; r ecx’

0:000> j (MySymbol=0) ”; ‘r ebx; r ecx’

0:000> j (MySymbol=0)  ; ‘r ebx; r ecx’

  1.  

g

gc

如果在条件断点之中,应使用gc命令,这样如果通过step或trace进入断点,那么出断点gc维持step或trace命令;而g命令无论怎么进入断点都使用g命令执行。

0:000>   bp Address "j (Condition) ‘OptionalCommands’; ‘gc’ "

0:000>   bp Address "j (Condition) ‘OptionalCommands’; ‘g’ "

  1.  

gu

The   gu command causes the target to execute until the current function is   complete.

User-Mode Syntax

[~Thread] gu

Kernel-Mode Syntax

gu  

  1.  

![sos.]findappdomain

!findappdomain <object address>

查找对象所在的AppDomain

  1.  

sx, sxd, sxe,   sxi, sxn, sxr

sx,   sxd, sxe, sxi, sxn, sxr (Set Exceptions)

设置异常发生后调试器的行为。

sx

sx{e|d|i|n} [-c   "Cmd1"] [-c2 "Cmd2"] [-h] {Exception|Event|*}

sxr

sx查看系统异常处理列表;

sxr重置系统异常处理列表;

 

sxe  

Break   (Enabled)  When this exception occurs,   the target immediately breaks into the debugger before any other error   handlers are activated. This kind of handling is called first chance   handling.

 

sxd

Second   chance break (Disabled). The debugger does not break for a first-chance   exception of this type (although a message is displayed). If other error   handlers do not address this exception, execution stops and the target breaks   into the debugger. This kind of handling is called second chance handling.

 

sxn

Output   (Notify). When this exception occurs, the target application does not break   into the debugger at all. However, a message is displayed that notifies the   user of this exception.

 

sxi

Ignore   When this exception occurs, the target application does not break into the   debugger at all, and no message is displayed.

  1.  

#

#   (Search for Disassembly Pattern)

# [Pattern] [Address [ L   Size ]]

示例:

#  mov    0040116b

#  8945*    0040116b

#  116d    0040116b

# strlen main

例如:在反汇编“8b4510          mov     eax,dword ptr [ebp+10h]”查找[ebp+10h],命令如下:

#   \[ebp\+10h\] 7c810000

注意,这个命令进行的是形式匹配!因此要求先查看U命令的输出结果。

  1.  

ld

ld   (Load Symbols)

ld   [ModuleName]

加载调试信息。在对付lazy load的dll时比较有用。

  1.  

ln

ln   (List Nearest Symbols)

ln   Address

The   ln command displays the symbols at or near the given address.

  1.  

ls, lsa

ls,   lsa (List Source Lines)

ls   [.] [first] [, count]

lsa   [.] address [, first [, count]]

  1.  

.context

.context   (Set User-Mode Address Context)

.context   [PageDirectoryBase]

PDB地址来自.process命令

kd>   !process 0 0

PROCESS   fe3c0d60  SessionId: 0  Cid: 0208      Peb: 7ffdf000  ParentCid: 00d4

    DirBase: 0011f000  ObjectTable: fe3d0f48    TableSize:  30.

Image: regsvc.exe

  1.  

.process

.process   (Set Process Context)

The   .process command specifies which process is used for the process context.

Syntax

.process   [/i] [/p [/r] ] [/P] [Process]

  1.  

.thread

.thread   (Set Register Context)

The   .thread command specifies which thread will be used for the register context.

Syntax

.thread   [/p [/r] ] [/P] [/w] [Thread]

  1.  

.time

.time (Display System Time)

0:000> .time

Debug session time: Mon Apr 07 19:10:50 2003

System Uptime: 4 days   4:53:56.461   OS已经运行了多久

Process Uptime: 0 days   0:00:08.750  进程已经运行了多久

  Kernel time: 0 days   0:00:00.015

  User time: 0 days   0:00:00.015

  1.  

.ttime

The   .ttime command displays the running times for a thread.

0:000>   .ttime

Created: Sat Jun 28 17:58:42 2003

Kernel:  0 days 0:00:00.131

User:    0 days 0:00:02.109

  1.  

.writemem

.writemem   FileName Range

  1.  

![exts.]peb

The   !peb extension displays a formatted view of the information in the process   environment block (PEB).

Syntax

!peb   [PEB-Address]

  1.  

![exts.]teb

The   !teb extension displays a formatted view of the information in the thread   environment block (TEB).

Syntax

!teb   [TEB-Address]

  1.  

![exts.]tls

The   !tls extension displays a thread local storage (TLS) slot.

Syntax

    !tls Slot [TEB]

Parameters

Slot  

Specifies   the TLS slot. This can be any value between 0 and 1088 (decimal). If Slot is -1, all slots are displayed.

TEB  

Specifies   the thread environment block (TEB). If this is 0 or   omitted, the current thread is used.

  1.  

![ntsdexts.]locks

!locks   [-v][-o]              – Dump all Critical   Sections in process

 

1) 找到用户登陆的调用堆栈

2.   FOR /L %i IN (1,1,100) DO type
test.txt >> testdata.txt

2.2.    常用命令

得到dump文件:

Adplus -hang -pn w3wp.exe -quiet (hang)   [-FullOnFirst]

adplus -crash -pn w3wp.exe (crash)

 

 

加载 sos 扩展功能dll:

.load sos.dll

 

.loadby sos mscorwks

 

自动分析

!analyze –v

 

!analyze
-hang
。这个扩展将会执行一个线程栈分析以确定是不是有哪些线程正在阻塞其他线程。

 

查看版本:

!eeversion

 

查看异常:

!dumpallexceptions

 

运行 help 命令:

!help

!help [command name]   //查找某个命令更多详细的内容, 如: !help
eeversion

 

显示系统与时间有关的信息:

.time

 

dump文件时cpu的使用情况。同时可以得到其他的有用的信息(例如:等待请求队列的数量,已完成的线程和时间):

!threadpool

 

列出当前正在运行的线程和cpu使用情况

!runaway                 //比如查看哪个线程占用 CPU 时间过多

 

列表显示出应用程序所有正在运行的线程,当前应用程序域中后台正在运行的程序,等等线程相关内容

!threads

 

切换到特定的线程上查看相关内容

~[thread id]s  //例如: ~2s

 

列出当前线程中的调用栈信息

!clrstack     //如果想查看额外的信息,添加 “-p”选项, 如: !clrstack –p

!clrstack -a

 

显示指定地址对象的内容:

!dumpobj 0xf32fcc              //(!do 为简写)

 

继续查看对象的值

!dumpobj -v 0xf32fcc

 

!dumpvc 7910c878  01573774  //mt  value

 

查看当前特定线程的堆栈中所有托管对象

!dumpstackobjects (或 简称!dso )           //next:  !dumpobj 0x1d298ad8

 

得到当前线程对象中关于数组对象的详细信息

!dumparray [address]     (简称!da)         //!do
[address]仅得到简单信息

!da –details [address]  //详细信息

 

得到对象的整个大小

!objsize 071bef70

!objsize poi(0x61b47d4+0xc)      //Address + Offset

.foreach (obj {!dumpheap -mt 0x0c2eaeb4 -short}){!objsize ${obj}}  
//0x0c2eaeb4: mt

.foreach(myobj {!dumpheap -short -min 85000}) {!objsize myobj}
//所有所有对象中大于85K内容

 

dump出所有的托管堆上的对象

!dumpheap –stat             //使用 –stat 参数来得到托管堆上的摘要信息

 

参数–mt (即:MethodTable):

!dumpheap -mt 793308ec      //next: 进一步查看对象,使用 !dumpobj
[object address]即可

 

参数–mt:

!dumpheap –type System      //出许多包含 System 名称的对象

 

 

参数是 –min \ -max ,该参数接受一个最小\最大的对象字节数:

!dumpheap –stat –min 85000          //查看大于85000bytes字节的对象

!dumpheap -min 85000

!dumpheap -mt 790fd8c4 -min 20000 -max 25000

 

!dumpheap -type System.String -min 150 -max 200  //检查大小在 150 至 200
之间的所有字符串

!dumpheap -mt 790fd8c4 -strings   //只输出字符串

 

参数-short:

!dumpheap -type System.String -min 6500 –short 
//仅仅查询出对象的地址信息

 

.foreach语句:

.foreach (myAddr {!dumpheap -type System.String -min 6500
-short}){!dumpobj myAddr;.echo
**************************}        // .echo
命令打印分割符

 

.shell命令:

.shell -i – -ci “!iisinfo.clientconns” FIND /c “Request active”

.shell -i – -ci “!iisinfo.clientconns” FIND /c “<table”

 

 

!dumpobj 04aa1a90     // 查看对象的详细信息

 !dumpclass 0x6c632e8  // 查看类型的详细信息

 !dumpmt -md 0x09750a8 // 查看方法表的详细信息

 !dumpmd 0x00975070    // 查看方法表项的方法描述的详细信息

 u 0x79b7c4eb          // 反汇编指定地址的指令

[]

 

查看进程程序集加载情况:

!dumpdomain

 

将进程程序集导出

!SaveModule 00992c5c z:\temp\a.dll

 

显示模块信息:

!dumpmodule [-mt] 1c5a1098   //1c5a1098: module address

 

 

查看native内存内容:

MetaData start address: 1d3b09e4 (4184 bytes)

dc 1d3b09e4 1d3b09e4+0n4184

du 3cd30038 3cd30038 +1000

 

[异常]

 

查看托管线程

!threads

 

查看所有的Exception:

!dumpheap –type Exception

 

命令将打印出当前堆栈上正在被抛出的exception

!pe/!PrintException

 

打印出相应命令的详细信息及堆栈:

!pe address

 

查看某个具体的异常的详细信息

!do [address]

 

!do –nofields [address] //string类型简单输出格式

 

查看对象的信息:

!gcroot [address]

 

打印出所有的同一个类型的Exception的信息

.foreach(myVariable {!dumpheap -type System.ArgumentNullException
-short}){!pe myVariable;.echo **}                

//OutOfMemoryException? StackOverflowException?
System.OutOfMemoryException?

 

列出了GC 堆的大小 和G0,G1,G2 ,LOH的开始地址:

!EEHeap [-gc] [-loader]

!eeheap

!eeheap –gc

!eeheap -loader

 

查看内存信息

!name2ee * WindbgDemo.Program

 

查看方法描述

!dumpmd 00993034

 

查看方法表信息

!dumpmt -md 0099304c

 

查看il

!dumpil 00993034

 

Dump文件

C:\Program Files\Debugging Tools for Windows (x86) adplus.vbs -hang -o
C:\dump -p 6876

参数说明:

?-hang: 表示附加到目标进程,抓取 dump 镜像,然后解除。对应的参数是
-crash 崩溃模式,该参数会终止目标进程。

?-o: 指定 Dump 文件保存路径。

?-p: 指定目标进程 PID。

 

 

Jitted代码:

  1. sxe ld:mscorjit.dll
  2. 2.    .loadby sos mscorwks
  3. !name2ee test.exe System.Program.Main
  4. ba w4 975070+0x04 “bp poi(975070+0x04);g”
  5. !clrstack

 

Ngened代码:

 

 

1、!analyze -v :用于分析挂掉线程的详细情形,错误原因。

2、!locks :列出全部资源使用情况。

3、!locks -v 0x???????? :特定地址的死锁分析。

4、!thread 0x????????:特定线程详情。

5、.thread 0x????????:转到某个线程堆栈。

 

0:071> kb

 

2.3.    ~

查看系统当前线程,使用~*s命令切换线程,如需要切换到8号线程,可以使用命令:~8s

0:004> ~

   0  Id: dd0.7c0 Suspend: 1 Teb: 7ffdf000 Unfrozen

   1  Id: dd0.1230 Suspend: 1 Teb: 7ffde000 Unfrozen

   2  Id: dd0.bc4 Suspend: 1 Teb: 7ffdd000 Unfrozen

   3  Id: dd0.1424 Suspend: 1 Teb: 7ffdc000 Unfrozen

.  4  Id: dd0.ba8 Suspend: 1 Teb: 7ffdb000 Unfrozen

 

RetAddr           : Args to
Child                                                           : Call
Site

第一个命令将Windows文件夹里面所有子文件夹的文件列表都输出到test.txt文件里,第二个命令循环100遍,将test.txt文件内容追加到testdata.txt里面,这样就可以生成好几百兆大小的文本文件了。

2.4.    .load sos

加载微软提供的调试扩展工具,可以更方便的调试托管代码。只有加载SOS之后,才能使用下面的这些命令。

0:004> .load sos

000007fe`f81889e5 :
00000000`0d379e30 00000000`0d379490 00000000`0d378ba0
00000000`00000000 : MSVCR100_CLR0400!memcpy+0x266

 

2.5.    !clrstack

查看当前线程的调用栈,如果想查看所有进程的调用栈情况, 使用~*e
!clrstack

0:000> !clrstack

OS Thread Id: 0x7c0 (0)

ESP       EIP    

002aee00 775564f4 [NDirectMethodFrameStandalone: 002aee00]
System.Windows.Forms.SafeNativeMethods.MessageBox(System.Runtime.InteropServices.HandleRef,
System.String, System.String, Int32)

002aee1c 66fffd28
System.Windows.Forms.MessageBox.ShowCore(System.Windows.Forms.IWin32Window,
System.String, System.String, System.Windows.Forms.MessageBoxButtons,
System.Windows.Forms.MessageBoxIcon,
System.Windows.Forms.MessageBoxDefaultButton,
System.Windows.Forms.MessageBoxOptions, Boolean)

002aeebc 66fff93e System.Windows.Forms.MessageBox.Show(System.String)

002aeec4 006603ab TestCode4AQTime.Form1.button1_Click(System.Object,
System.EventArgs)

002aeee0 669d4170 System.Windows.Forms.Control.OnClick(System.EventArgs)

002af04c 66a023b0
System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)

002af058 66a084a0

002af218 007c09e4 [NDirectMethodFrameStandalone: 002af218]
System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)

002af228 66a18aee
System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32,
Int32, Int32)

002af2c4 66a18757
System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32,
System.Windows.Forms.ApplicationContext)

002af318 66a185a1
System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32,
System.Windows.Forms.ApplicationContext)

002af348 669d5911
System.Windows.Forms.Application.Run(System.Windows.Forms.Form)

002af35c 006600ae TestCode4AQTime.Program.Main()

000007fe`f818915c :
00000000`0d378b70 000007fe`f8251a30 00000000`0000252c
000007fe`f398d1c8 :
clr!StackFrameIterator::ProcessCurrentFrame+0x56a

数据生成好了以后,使用执行性能测试—起步贴出来的程序执行一遍,就可以看到OutOfMemeoryException了(如果你没有看到这个异常,可能是你的机器太强大了,请换一个更大的文件)。在调试器里面执行后出现异常后,加载进SOS进行分析:

2.6.    !do(DumpObj)

查看class对象内容。!do [对象的内存地址]

0:000> !do 01e80d5c

Name: System.String

MethodTable: 68eb88c0

EEClass: 68c7a498

Size: 102(0x66) bytes

 (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

String: system.serviceModel.activation/diagnostics

Fields:

      MT    Field   Offset                 Type VT     Attr    Value
Name

68ebab0c  4000096        4         System.Int32  1 instance       43
m_arrayLength

68ebab0c  4000097        8         System.Int32  1 instance       42
m_stringLength

68eb95a0  4000098        c          System.Char  1 instance       73
m_firstChar

68eb88c0  4000099       10        System.String  0   shared   static
Empty

    >> Domain:Value  003a5698:01e61198 <<

68eb94f0  400009a       14        System.Char[]  0   shared   static
WhitespaceChars

    >> Domain:Value  003a5698:01e6174c <<

!dumpvc(dv) 命令查看Value类型对象,对应c#的struct对象

000007fe`f818936a :
00000000`00000000 00000000`0d378b70 000007fe`f398d19c
00000000`0d378b70 : clr!StackFrameIterator::NextRaw+0x35c

 

2.7.    !dso(DumpStackObjects)

查看栈上对象。

0:000> !dso

OS Thread Id: 0x7c0 (0)

ESP/REG  Object   Name

002aede0 01e61198 System.String   

002aede4 01e995c4 System.String    Finished

002aedf8 01e9760c System.Windows.Forms.MouseEventArgs

002aee4c 01e995c4 System.String    Finished

002aee90 01e9760c System.Windows.Forms.MouseEventArgs

002aee94 01e995c4 System.String    Finished

002aee98 01e87bfc System.Windows.Forms.Button

002aeeb8 01e61198 System.String   

002aeebc 01e88af0 System.EventHandler

002aeec4 01e69a54 TestCode4AQTime.Form1

002aeed0 01e87bfc System.Windows.Forms.Button

002aeedc 01e9760c System.Windows.Forms.MouseEventArgs

002aeee0 01e87d98 System.ComponentModel.EventHandlerList

000007fe`f8188d8c :
00000000`0d379420 00000000`00000002 00000000`00000002
00000000`0d378f40 : clr!StackFrameIterator::Next+0x22

 (318.1688): CLR
exception – code e0434352 (first chance)

2.8.    !dumpheap

查看托管堆内存对象信息。

!dumpheap –stat  只输出统计信息(常用

!dumpheap –type <partial type name>
只输出类型名和给出(部分)类型名称匹配的对象

0:000> !dumpheap -stat

total 4843 objects

Statistics:

      MT    Count    TotalSize Class Name

68ebaa5c       36         3776 System.Int32[]

68728c6c       95         5320 System.Configuration.FactoryRecord

68ebb010       32        10224 System.Collections.Hashtable+bucket[]

68eb94f0       41        10544 System.Char[]

68eba468      666        15984 System.Version

68ebb330       17        25528 System.Byte[]

68e94eec      231        41168 System.Object[]

68eb88c0     1102        89616 System.String

Total 4843 objects

000007fe`f826e2a7 :
00000000`0e0936d0 00000000`0e0936d0 00000000`00000000
00000000`00000000 : clr!Thread::StackWalkFramesEx+0x9e

… …

2.9.    !objsize

查看对象实际占用内存大小

0:000> !objsize 01e80d5c

sizeof(01e80d5c) =          104 (        0x68) bytes (System.String)

000007fe`f83350f0 :
00006dae`0a62919f 00000000`0e0936d0 00000000`00000000
00000000`00000000 : clr!Thread::ReadyForAsyncException+0x197

#

2.10. !gcroot

查找对象引用关系

0:000> !gcroot 01e80d5c

Note: Roots found on stacks may be false positives. Run “!help gcroot”
for

more info.

Scan Thread 0 OSTHread 7c0

Scan Thread 2 OSTHread bc4

DOMAIN(003A5698):HANDLE(Pinned):2f13e4:Root:02e65dd8(System.Object[])->

01e6d8cc(System.Configuration.ClientConfigurationSystem)->

01e6f22c(System.Configuration.RuntimeConfigurationRecord)->

01e7481c(System.Collections.Hashtable)->

01e7e760(System.Collections.Hashtable+bucket[])

000007fe`f833503b :
00000000`0e0936d0 00000000`00000028 00000000`0e0936d0
000007fe`f87cec00 : clr!Thread::HandleThreadAbort+0x6c

#
OutOfMemeoryException已经抛出了

2.11. !da(DumpArray)

查看数组对象

0:000> !da 02e65dd8

Name: System.Object[]

MethodTable: 68e94eec

EEClass: 68c7a8a0

Size: 4096(0x1000) bytes

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

Element Methodtable: 68eb84dc

[0] null

[1] null

[2] 01e88f00

[3] null

[4] null

[5] 01e88ef4

[6] null

[7] null

[8] null

000007fe`f83352d7 :
00000000`00000000 00000000`037bc050 00000000`0e0936d0
000007fe`f87cece0 : clr!CommonTripThread+0x18

#

2.12. !threads

列出当前进程中的托管线程,可以查看托管线程和系统线程的对应关系。

0:004> !threads

ThreadCount: 2

UnstartedThread: 0

BackgroundThread: 1

PendingThread: 0

DeadThread: 0

Hosted Runtime: no

                                      PreEmptive   GC Alloc      
    Lock

       ID OSID ThreadOBJ    State     GC       Context       Domain  
Count APT Exception

   0    1  e6c 0026aeb8      6020 Enabled  018bd010:018bdfe8
00265698     0 STA

   2    2  c08 002790f0      b220 Enabled  00000000:00000000
00265698     0 MTA (Finalizer)

 

000007fe`f81b6615 :
000007fe`f81b6470 00000000`00000000 00000000`00001ad6
00000001`00000028 : clr!FC_GCPoll+0x107

 (318.1688): CLR
exception – code e0434352 (!!! second chance !!!)

2.13. !eeheap

通过!eeheap,可以查看CLR堆的使用情况。

!eeheap –gc:只查看GC堆的输出结果。

!eeheap –loader:查看和AppDomains关联的各种私有堆的输出结果。

 

0:004> !eeheap -loader

Loader Heap:


System Domain: 726ee1f8

LowFrequencyHeap: Size: 0x0(0)bytes.

HighFrequencyHeap: 00232000(8000:1000) Size: 0x1000(4096)bytes.

StubHeap: 0023a000(2000:2000) 00c50000(10000:3000) Size:
0x5000(20480)bytes.

Virtual Call Stub Heap:

  IndcellHeap: Size: 0x0(0)bytes.

  LookupHeap: Size: 0x0(0)bytes.

  ResolveHeap: Size: 0x0(0)bytes.

  DispatchHeap: Size: 0x0(0)bytes.

  CacheEntryHeap: Size: 0x0(0)bytes.

Total size: 0x6000(24576)bytes


Shared Domain: 726edb48

LowFrequencyHeap: 00430000(2000:1000) Size: 0x1000(4096)bytes.

HighFrequencyHeap: 00432000(8000:1000) Size: 0x1000(4096)bytes.

StubHeap: 0043a000(2000:1000) Size: 0x1000(4096)bytes.

Virtual Call Stub Heap:

  IndcellHeap: 00480000(2000:1000) Size: 0x1000(4096)bytes.

  LookupHeap: 00485000(2000:1000) Size: 0x1000(4096)bytes.

  ResolveHeap: 0048b000(5000:1000) Size: 0x1000(4096)bytes.

  DispatchHeap: 00487000(4000:1000) Size: 0x1000(4096)bytes.

  CacheEntryHeap: 00482000(3000:1000) Size: 0x1000(4096)bytes.

Total size: 0x7000(28672)bytes


Domain 1: 265698

LowFrequencyHeap: 00410000(2000:2000) 006c0000(10000:1000) Size:
0x3000(12288)bytes.

HighFrequencyHeap: 00412000(8000:5000) Size: 0x5000(20480)bytes.

StubHeap: 0041a000(2000:1000) Size: 0x1000(4096)bytes.

Virtual Call Stub Heap:

  IndcellHeap: 00420000(2000:1000) Size: 0x1000(4096)bytes.

  LookupHeap: 00426000(1000:1000) Size: 0x1000(4096)bytes.

  ResolveHeap: 0042a000(6000:2000) Size: 0x2000(8192)bytes.

  DispatchHeap: 00427000(3000:1000) Size: 0x1000(4096)bytes.

  CacheEntryHeap: 00422000(4000:1000) Size: 0x1000(4096)bytes.

Total size: 0xe000(57344)bytes


Jit code heap:

LoaderCodeHeap: 004e0000(10000:1000) Size: 0x1000(4096)bytes.

Total size: 0x1000(4096)bytes


Module Thunk heaps:

Module 72741000: Size: 0x0(0)bytes.

Module 00432358: Size: 0x0(0)bytes.

Module 00432010: Size: 0x0(0)bytes.

Module 00412c5c: Size: 0x0(0)bytes.

Module 66701000: Size: 0x0(0)bytes.

Module 672e1000: Size: 0x0(0)bytes.

Module 67f61000: Size: 0x0(0)bytes.

Module 65ab1000: Size: 0x0(0)bytes.

Module 65571000: Size: 0x0(0)bytes.

Total size: 0x0(0)bytes


Module Lookup Table heaps:

Module 72741000: Size: 0x0(0)bytes.

Module 00432358: Size: 0x0(0)bytes.

Module 00432010: Size: 0x0(0)bytes.

Module 00412c5c: Size: 0x0(0)bytes.

Module 66701000: Size: 0x0(0)bytes.

Module 672e1000: Size: 0x0(0)bytes.

Module 67f61000: Size: 0x0(0)bytes.

Module 65ab1000: Size: 0x0(0)bytes.

Module 65571000: Size: 0x0(0)bytes.

Total size: 0x0(0)bytes


Total LoaderHeap size: 0x1c000(114688)bytes

=======================================

 

0:004> !eeheap -gc

Number of GC Heaps: 1

generation 0 starts at 0x01881018

generation 1 starts at 0x0188100c

generation 2 starts at 0x01881000

ephemeral segment allocation context: none

 segment    begin allocated     size

01880000 01881000  018bdff4 0x0003cff4(249844)

Large object heap starts at 0x02881000

 segment    begin allocated     size

02880000 02881000  02886de8 0x00005de8(24040)

Total Size   0x42ddc(273884)


GC Heap Size   0x42ddc(273884)

 

000007fe`f398d1c8 :
00000001`5ff74050 00000000`00001296 00000001`5ff76c08
00000000`00000000 : clr!Buffer::BlockCopy+0x1a5

eax=0017eb74
ebx=00000005 ecx=00000005 edx=00000000 esi=0017ec20 edi=005595e0

2.14. 转储进程

直接使用微软提供的aplus.vbs脚本转储w3wp.exe进程

Cscript adplus.vbs –hang –pn w3wp.exe –o <Dump存储目录> -quiet –do

使用Windbg命令转储进程

.dump /f <Dump存储文件>

000007fe`f36b932a :
00006dae`d26d74e7 000007fe`f87cdfc0 00000000`0d37dce8
00000000`00000000 :
System_Xml_ni!System.Xml.XmlTextReaderImpl.ShiftBuffer(Int32, Int32,
Int32)+0x28

eip=753b9617
esp=0017eb74 ebp=0017ebc4 iopl=0         nv up ei pl nz ac pe nc

第三章 常见问题

000007fe`f36b8c6d :
00000000`0d37b4d0 00000001`5ff76c08 00000014`00054cfe
000007fe`f718f13e :
System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseText(Int32 ByRef,
Int32 ByRef, Int32 ByRef)+0x19a

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000    
        efl=00000216

3.1. OutOfMemory

解决思路:

使用!dumpheap –stat命令检查托管堆对象。

使用gcroot查找数量/大小不正常对象的引用链,以明确对象没被GC回收的原因

000007fe`f36b8a87 :
00000002`3067ffe0 000007fe`f36c0cdb 000007fe`f37578c0
00000002`30680008 :
System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseText()+0x6d

KERNELBASE!RaiseException+0x54:

3.2. 系统缓慢&HighCPU&单点效率异常

解决思路:

使用!clrstack命令,多次抓取系统调用栈。

比较调用栈停留位置,找到可疑处。

通过!do&!dso查看栈变量,判断代码调用栈停留位置效率低下原因。

 

 

 

 

000007fe`f36c02f9 :
00000001`5ff740b0 000007fe`f36bfd6d 00000001`5ff74060
00000001`5ff74050 :
System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseElementContent()+0x27

753b9617
c9              leave

第四章 应用示例

 

示例1: 缓存对象

 

  1. 检查缓存大小

 

!dumpheap –stat –type System.Web.Caching.Cache  //得到了
System.Web.Caching.Cache 对象的方法表

 

!dumpheap –mt 1230494c     //得到1230494c方法表中所有对象

 

!objsize 03392d20       //得到当前地址对应对象的大小

 

  1. 什么内容被缓存了?

 

!dumpheap –stat –type System.Web.Caching   //查看 CacheEntrys 对象

 

!dumpheap -mt 12306320  //查看CacheEntrys的方法表MT

 

!do 076b42dc          //检查对象的所有内容, 之后

 

示例2: 挂起

检查堆栈信息

 

检查本地的堆栈信息:

~* kb 2000

 

检查 dotnet 堆栈信息:

~* e!clrstack                   // Do you see any patterns or recognize
any of the callstacks that suggests a thread is waiting for a
synchronization mechanism?

 

  看看有多少调用堆栈里有Monitor.Enter:

.shell -ci “~* e !clrstack” FIND /C Monitor.Enter

 

跟踪诊断挂起现象

 

检查等待锁的线程ID列表:

!syncblk                    //(提示:MonitorHeld = 1
代表拥有者,2为等待者)

 

查看一个等待线程的状态:

~5s         (切换到线程 5,用真实的线程ID替换5即可)
kb 2000   (检查本地堆栈信息)
!clrstack  (查看dotnet 堆栈信息)

!clrstack –p (查看dotnet 堆栈信息,包括参数内存地址)

!clrstack –a

 

 

示例3: 查看缓存占用情况

查看Cache占用内存情况:

!name2ee System.Web.dll System.Web.Caching.Cache

0:000> !name2ee System.Web.dll System.Web.Caching.Cache

Module: 65f21000 (System.Web.dll)

Token: 0x020000fa

MethodTable: 66148d24

EEClass: 65f86838

Name: System.Web.Caching.Cache

 

!dumpheap –mt [MethodTable]  //查看托管堆中对象类型

 

!objsize 06952248  //查看对象大小

 

示例4: 内存调试

 

首先看看GC heap的大小,和dump 文件比较一下。

这个 !eeheap 命令列出了GC 堆的大小 和G0,G1,G2 ,LOH的开始地址。

 

0:001> !eeheap –gc

generation 0 starts at 0x0110be64

generation 1 starts at 0x01109cd8

generation 2 starts at 0x01021028

 segment    begin allocated     size

01020000 01021028 0110de70 000ece48(970312)

Total Size   0xece48(970312)


large block 0x11e1fc04(300022788)

large_np_objects start at 17b90008

large_p_objects start at 02020008


GC Heap Size 0x11f0ca4c(300993100)

 

在这里GC 堆是300M左右,dump文件是358M。

 

使用 !dumpheap –stat 命令来查看占用了空间的托管对象。

0:001> !dumpheap -stat

Bad MethodTable for Obj at 0110d2a4

Last good object: 0110d280

total 14459 objects

Statistics:

      MT    Count TotalSize Class Name

3c6185c        1        12 System.Web.UI.ValidatorCollection

3c2e110        1        12
System.Web.Configuration.MachineKeyConfigHandler

3c29778        1        12
System.Web.Configuration.HttpCapabilitiesSectionHandler

3c23240        1        12 System.Web.SafeStringResource

… …

D12f28      1133     46052 System.Object[]

153cb0        88     76216 Free

321b278       85    178972 System.Byte[]

d141b0      6612    416720 System.String

Total 14459 objects

 

使用!gcroot 16220018从LOH上得到更多对象的信息:

0:001> !gcroot 16220018

Scan Thread 1 (4e8)

Scan Thread 5 (bb0)

Scan Thread 6 (d0)

Scan Thread 10 (43c)

Scan Thread 11 (308)

Scan Thread 12 (6e4)

Scan HandleTable 14e340

Scan HandleTable 150e40

Scan HandleTable 1a6fa8

HANDLE(Strong):37411d8:Root:020784d8(System.Object[])-

>0108b504(System.Web.HttpRuntime)->0108b9d0(System.Web.Caching.CacheSingle)-

>0108ca68(System.Web.Caching.CacheUsage)->0108ca78(System.Object[])-

>0108cb3c(System.Web.Caching.UsageBucket)-

>010f95fc(System.Web.Caching.UsageEntry[])-

>01109be8 (System.Web.Caching.CacheEntry)->00000000()

 

要找出System.Web.Caching.Cache的地址,请使用
!name2ee命令,这个命令接受2个参数 程序集的名字和全类名:

0:001> !name2ee System.Web.dll System.Web.Caching.Cache


MethodTable: 03887998

EEClass: 03768814

Name: System.Web.Caching.Cache


 

EEClass 是一个用来表示.net 类的内部结构。

取得托管堆中的某个对象的类型,使用 !dumpheap –mt MethodTable地址
的方式来获得:

0:001> !dumpheap -mt 03887998

 Address       MT     Size

0108b8ac 03887998       12

Bad MethodTable for Obj at 0110d2a4

Last good object: 0110d280

total 1 objects

Statistics:

      MT    Count TotalSize Class Name

 3887998        1        12 System.Web.Caching.Cache

Total 1 objects

large objects

 Address       MT     Size

total 0 large objects

 

查看 System.Web.Caching.Cache 的大小,使用 !objsize 0108b8ac:

0:001> !objsize 0108b8ac

sizeof(0108b8ac) = 300126128 (0x11e38fb0) bytes
(System.Web.Caching.Cache)

 

修正代码,把缓存移除。重新装载 dump文件,加载模块,使用 !eeheap –gc
来看看托管堆的大小:

0:000> !eeheap -gc

generation 0 starts at 0x012cc0e4

generation 1 starts at 0x012afde8

generation 2 starts at 0x011c1028

 segment    begin allocated     size

011c0000 011c1028 012d6000 00114fd8(1134552)

Total Size 0x114fd8(1134552)


large block 0x8060(32864)

large_np_objects start at 00000000

large_p_objects start at 021c0008


GC Heap Size 0x11d038(1167416)

dump 显示 GC 堆的大小是1M,不是222M,这表示除了1M其它的都被收集了。

 

示例5: 内存泄露跟踪

I.查看内存使用概要:

!address -summary

 

0:000> !address -summary

 

——————–   Usage SUMMARY ————————–

 

    TotSize (      KB)     Pct(Tots) Pct(Busy)   Usage

 

   373b7000 (    904924) : 21.58%    85.85%    : RegionUsageIsVAD

 

   bfa89000 ( 3140132) : 74.87%    00.00%      : RegionUsageFree

 

    76e6000 (    121752) : 02.90%    11.55%    : RegionUsageImage

 

     67c000 (    6640) : 00.16%    00.63%      : RegionUsageStack

 

          0 (       0) : 00.00%    00.00%      : RegionUsageTeb

 

    144a000 (   20776) : 00.50%    01.97%      : RegionUsageHeap

 

          0 (       0) : 00.00%    00.00%      : RegionUsagePageHeap

 

       1000 (       4) : 00.00%    00.00%      : RegionUsagePeb

 

       1000 (       4) : 00.00%    00.00%      : RegionUsageProcessParametrs

 

       2000 (       8) : 00.00%    00.00%      : RegionUsageEnvironmentBlock

 

       Tot: ffff0000 (4194240 KB) Busy:   40567000 (1054108 KB)

 

——————–   Type SUMMARY ————————–

 

    TotSize (      KB)     Pct(Tots)  Usage

 

   bfa89000 ( 3140132) : 74.87%   :

 

    834e000 (    134456) : 03.21%   : MEM_IMAGE  [总共内存占用百分比]

 

     95a000 (    9576) : 00.23%   : MEM_MAPPED

 

   378bf000 (    910076) : 21.70%   : MEM_PRIVATE

 

——————–   State SUMMARY ————————–

 

    TotSize (      KB)     Pct(Tots)  Usage

 

   34bea000 (    864168) : 20.60%   : MEM_COMMIT

 

   bfa89000 ( 3140132) : 74.87%   : MEM_FREE

 

    b97d000 (    189940) : 04.53%   : MEM_RESERVE

 

Largest free   region: Base 80010000 – Size 7fefa000 (2096104 KB)

 

  这里有非常多的信息,但所有的这些都不是这么明显的。相信我,让我们花一些时间在这里。在任何一个案例中,我用这个来帮助我指出我需要查看哪些地方,所以我不介意它要花费多少,即使就是一个概述的结果。

 

一些要注意的地方:

 

上面一屏显示了按照类型不同而分类显示的由进程使用的内存。第一部分是按照区域类型来划分的,它按照什么样子的分配类型告诉你信息。最常遇到的一个类型是VAD = Virtual Alloc, Image = dlls 和 exes,Heap = heaps the process owns,从WinDbg的帮助中可以得到更多的信息。接着下面是按IMAGE, MAPPED  或 PRIVATE 的类型来列出,最后一部分是按已提交(also committed,就是指实际已经分配的)或保留(reserved)的方式来列出它们。

RegionUsageheap,代表的是NT heaps;MEM_COMMIT和MEM_RESERVE加起来,是virtual   memory。

 

Tot: ffff0000 (4   194 240 kb) :的意思是我总共有4GB的虚拟内存地址空间提供给这个应用程序。32位系统上,你可以寻地4GB的空间,典型的是2GB的用户模式的内存空间,所以一般你会看到2GB而不是这里的4GB,在64位上,运行一个32位的进程会得到完全的4GB的空间,所以我这里看到的是4GB。

 

Busy: 40567000 (1   054 108 kb)  是我们已经使用的(已经分配的)。

 

MEM_PRIVATE是一个私有的内存,它不和其他进程共享内存,不是映射到文件的内存。不要把这个和性能计数器中的Private Bytes混淆。这里的MEM_PRIVATE 是保留+已提交(即已分配的)(reserved + committed)的字节数,另外那个Private   Bytes 是申请/已提交(allocated/committed)的字节数。

 

MEM_PRIVATE 是已经提交(已经分配)的内存(不一定是 private的),这个可能是最接近你得到的Private Bytes的。

 

MEM_RESERVE 是已经保留的,但没有实际分配的,未提交的内存。所有已经分配的内存也是定义为保留的,所以如果你查看所有保留的内存(最接近你得到的virtual bytes),你必须加上MEM_COMMIT和   MEM_RESERVE,它是显示在Busy 中的那个数字。你自己把数字加上后比对一下看看。

 

Q:哪个值最能代表如下两个指标?

 

·Private Bytes           A: MEM_COMMIT

 

·Virtual Bytes           A: Busy

 

Q:大部分的内存都去哪里了?(哪个区域)

 

A:在这里,大约904MB是为VAD保留的,VAD是dotnet对象存放的地方,因为GC堆是virtual   allocs 分配的。

 

Q:Busy,Pct(Busy),Pct(Tots)是什么意思?

 

A:Pct(Tots) 显示的是整个虚拟地址空间中分配给不同区域类型的百分比。Pct(Busy)显示的是保留的内存中分配给不同区域的百分比。Pct(busy) 很显然是我最关心的一个。

 

Q:MEM_IMAGE 是什么意思?

 

A:从帮助文件中我们知道:这个是表示从一个可执行的映射文件的一部分映射到的内存。换句话说 就是dll 或一个exe 文件的内存映射。

 

Q:哪个区域的.net 内存是适宜的,为什么?

 

A:在RegionUsageIsVAD,理由如上。

 

  从性能计数器中我们看到#Bytes   in all Heaps 跟随着Private bytes的增长而增长,那说明了内存的增加几乎都是.net 的使用而增加的,进而我们转化为为什么.net 的GC堆(heap)始终在增长。

 

 

II.运行 !eeheap –gc 来查看.net GC 堆的大小

0:000> !eeheap   -gc

 

Number of GC   Heaps: 2

 

——————————

 

Heap 0 (001aa148)

 

generation 0   starts at 0x32f0639c

 

generation 1   starts at 0x32ae3754

 

generation 2   starts at 0x02eb0038

 

ephemeral segment   allocation context: none

 

 segment      begin allocated     size

 

001bfe10   7a733370  7a754b98 0x00021828(137256)

 

001b0f10   790d8620  790f7d8c 0x0001f76c(128876)

 

…..

 

Large object heap   starts at 0x0aeb0038

 

 segment      begin allocated     size

 

0aeb0000 0aeb0038  0aec0b28 0x00010af0(68336)

 

Heap Size  0x15fd1310(368907024)

 

——————————

 

Heap 1 (001ab108)

 

generation 0   starts at 0x36e665bc

 

generation 1   starts at 0x36a28044

 

generation 2   starts at 0x06eb0038

 

ephemeral segment   allocation context: none

 

 segment      begin allocated     size

 

06eb0000   06eb0038  0aea58d4 0x03ff589c(67066012)

 

……

 

Large object heap   starts at 0x0ceb0038

 

 segment      begin allocated     size

 

0ceb0000   0ceb0038  0ceb0048 0x00000010(16)

 

Heap Size  0x15ab1570(363533680)

 

——————————

 

GC Heap Size  0x2ba82880(732440704)

 

 

 

Q:总共有多少个Heap,为什么?

 

A:这里有两个堆,因为我们运行在多核进程模型中。

 

Q:有多少内存被保存在了.net GC 堆中?拿#Bytes   in all Heaps比较一下。

 

A:GC的堆大小是:GC Heap Size 0x2ba82880(732 440 704),它和性能计数器中的bytes in all heaps很接近。

 

Q:large object heap 上有多少内存?提示:把large   object heap段上的合计加起来,和性能计数器中的Large Object Heap Size 比较一下。

 

A:它是非常小的,所以LOH看起来不是问题所在,大小是68 336 + 16 bytes

 

 

III. 运行 !dumpheap –stat 来输出所有的以统计式样表示的.net 对象

Q:查看 5 到10个使用了大部分内存的对象,思考一下是什么泄露了?

 

A:

 

66424cf4       37        57276 System.Web.Caching.ExpiresEntry[]

 

663b0cdc     4001       192048   System.Web.SessionState.InProcSessionState

 

7912d8f8     3784       255028 System.Object[]

 

7912d9bc      820       273384   System.Collections.Hashtable+bucket[]

 

6639e4c0     4037       290664 System.Web.Caching.CacheEntry

 

0fe11cf4    36000       576000 Link

 

790fdc5c    36161       723220 System.Text.StringBuilder

 

001a90c0     1105        7413924      Free

 

790fd8c4    51311      721773112 System.String

 

Total 163943   objects

 

大部分的内存是被strings用掉了,这个不太正常,虽然strings 在应用中是最常见的,但是大约721MB的显然有点怪异,并且有3600个Links(无论它们是什么),看起来有点奇怪。特别是因为有差不多数量的stringbuilds 出现在dump中。

 

Q:“size”那个行显示了什么?例如,“size”这行包含了什么?

 

A:如果我们用命令!do 把Link 对象输出来,我们看到有一个指针指向stringbuilder(url)和一个指针指向string(name),link对象的大小是16B,这个大小仅仅包含了指针的大小和其他一些开销(methos table 等)。

 

如果你运行 !objsize ,你会看见大小是高达20144   B ,这个大小是包含成员变量的,比如Link对象的大小和它引用的所有对象。

 

你看到了什么通过!dumpheap 输出的16B的每一个link。它不包含成员变量的大小是由一些不同原因的:

 

 1)它将要花费很长的时间去计算大小。

 

 2) 一些对象(假如是A和B)可能都指向C对象,如果你使用 !objsize 计算A 和B的大小,他们都会包含C的大小,所以size这个列的值会变得很复杂难以计算。

 

 3) 在这个例子中Link的大小size看起来似乎是正常的。因为一个link对象包含一个url和一个name。但是如果一个web 控件可能会包含一个成员变量 _parent ,如果你运运行   !objsize ,这样就会包含父对象(page)那就显然是不合适的。

 

0:000> !do   371d44cc

 

Name: Link

 

MethodTable:   0fe11cf4

 

EEClass: 0fde5824

 

Size: 16(0x10)   bytes

 

 (C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\

 

Temporary ASP.NET   Files\buggybits\b27906cc\f5f91899\App_Code.wbwztx_4.dll)

 

Fields:

 

      MT      Field   Offset                 Type VT     Attr      Value Name

 

790fdc5c  4000006        4 …ext.StringBuilder  0 instance 371d44dc url

 

790fd8c4  4000007        8        System.String  0 instance 02f13cd8 name

 

 

 

0:000>   !objsize 371d44cc

 

sizeof(371d44cc)   =        20144 (      0x4eb0) bytes (Link)

 

 

 

通常,我不推荐立刻查看在你的这个非常简单的dump文件中,在该命令输出的底部的strings,因为:

 

 · strings 这一行的“size”是实际的字符串string的有内容的真实大小。如果你和DataSet比较,这个“size”只是包含了行和列的指针,并没有包含行和列的内存。所以DataSet这个对象的大小几乎总是非常的小的。

 

 · string 字符串在大部分的对象中几乎是叶子节点,例如,dataset包含字符串,aspx页面包含字符串,session 变量也包含字符串。所以,在一个应用中几乎都是字符串。

 

然而在这个例子中,字符串有这么多,占有了那么多的内存。如果我们不查到其他一些阻止了我们的东西,那我们可能就要沿着string 这条路走下去了。

 

 

IV. 把各种不同大小的string 都输出来

得到string的 MT(method table),!dumpheap –stat 的输出结果的第一列。

!dumpheap -mt <string MT> -min 85000 -stat

!dumpheap -mt <string MT> -min 10000 -stat

!dumpheap -mt <string MT> -min 20000 -stat

!dumpheap -mt <string MT> -min 30000 -stat

!dumpheap -mt <string MT> -min 25000 -stat

  Q:大部分的string’在一个什么样的范围内?

   A:在 20000 和 25000 字节之间。

 

V. 把那个范围内的string 输出来。

 

   !dumpheap -mt <string MT> -min 20000 -max 25000

 

  
在这里,它们中的大部分是一模一样的大小的,这是一个指引我们向下前进的线索。

 

0:000> !dumpheap -mt 790fd8c4 -min 20000 -max 25000

 


 

Heap 0

 

 Address       MT     Size

 

02f1412c   790fd8c4    20020    

 

02f2d96c   790fd8c4    20020    

 

02f327c4   790fd8c4    20020    

 

02f3761c   790fd8c4    20020    

 

02f3c474   790fd8c4    20020    

 

02f412cc   790fd8c4    20020    

 

02f46124   790fd8c4    20020    

 

02f4af7c   790fd8c4    20020    

 

02f4fdd4   790fd8c4    20020    

 

02f54c2c   790fd8c4    20020    

 

 

VI.把它们中的一些输出来看看里面是什么

 

!do <address of string>  ,地址是 !dumpheap -mt 输出的第一列。

 

0:000> !do 02f327c4

String:

Q:这些string里面包含的是什么?

A:好像link.aspx 页面显示了link对象。

 

VII.
拣几个,看看它们被根化(rooted)到哪里(即为什么它们不会被回收)。注意你可能需要尝试不同的几个才行.

!gcroot <address of string>

 

0:000> !gcroot 02f327c4

 

Note: Roots found on stacks may be false positives. Run “!help gcroot”
for

 

more info.

 

Scan Thread 16 OSTHread 1948

 

Scan Thread 20 OSTHread 1b94

 

Scan Thread 21 OSTHread 1924

 

Scan Thread 22 OSTHread 188c

 

Scan Thread 14 OSTHread 1120

 

Scan Thread 24 OSTHread 13f8

 

Finalizer queue:Root:02f327a0(Link)->

 

02f327b0(System.Text.StringBuilder)->

 

02f327c4(System.String)

 

Q:它们被根化到哪里?为什么?

 

A:这个string是一个string builder
类型的成员变量,它表现的是一个link的成员变量(url),link
对象被根化在终结器队列中,那就是说他正在等待被终结。

 

检查终结器队列(finalizer queue)和终结线程(finalizer thread)

1)查看终结器队列

 

!finalizequeue

 

0:000> !finalizequeue

 

SyncBlocks to be   cleaned up: 0

 

MTA Interfaces to   be released: 0

 

STA Interfaces to   be released: 0

 

———————————-

 

——————————

 

Heap 0

 

generation 0 has   221 finalizable objects (0f44a764->0f44aad8)

 

generation 1 has   0 finalizable objects (0f44a764->0f44a764)

 

generation 2 has   45 finalizable objects (0f44a6b0->0f44a764)

 

Ready for   finalization 18009 objects (0f44aad8->0f45c43c)

 

——————————

 

Heap 1

 

generation 0 has   338 finalizable objects (0f45d840->0f45dd88)

 

generation 1 has   4 finalizable objects (0f45d830->0f45d840)

 

generation 2 has   36 finalizable objects (0f45d7a0->0f45d830)

 

Ready for   finalization 17707 objects (0f45dd88->0f46f234)

 

Statistics:

 

      MT      Count    TotalSize Class Name

 

663a1fc8        1           12   System.Web.Configuration.ImpersonateTokenRef

 

79116758        1           20   Microsoft.Win32.SafeHandles.SafeTokenHandle

 

791037c0        1           20   Microsoft.Win32.SafeHandles.SafeFileMappingHandle

 

79103764        1             20 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle

 

6639c104        1           20   System.Web.PerfInstanceDataHandle

 

663f6b5c        1           28   System.Web.Security.FileSecurityDescriptorWrapper

 

663a105c        1           32 System.Web.Compilation.CompilationMutex

 

7910b630        2           40   System.Security.Cryptography.SafeProvHandle

 

79112728        5          100   Microsoft.Win32.SafeHandles.SafeWaitHandle

 

790fe704        2          112 System.Threading.Thread

 

7910a5c4        2          120   System.Runtime.Remoting.Contexts

 

 

Q:在这个命令的输出中列出了什么对象?

A:所有具有终结/析构器的都被注册到终结器队列中,当对象被垃圾收集时,终结器会运行析构函数,否则在dispose函数中终结过程会挂起。

 

Q:有多少个对象是出于“ready for finalization”,它是什么意思?

A:大约有36000个,这些对象是要被垃圾收集的,正在等待被终结。如果ready for finalization大于0 但没有显示任何信息,这是一个说明终结器线程被堵塞的最好时机。所以这些对象被堵住了等待终结,他们消耗了大部分的内存。

 

2)  找出终结线程,了解它正在干什么,运行!threads ,在列出的线程中查找带有“(Finalizer)”的线程。

3)  切换到终结线程,检查托管的和本地(原生)的调用堆栈。

~5s   (把5 替换成真实的终结线程(finalizer thread)的ID号)

kb 2000

!clrstack

0:000>   !threads

 

  20      2 1b94 001ac2c0   200b220   Enabled  00000000:00000000   001ccc80     0 MTA (Finalizer)

 

 

 

0:020>   !clrstack

 

OS Thread Id:   0x1b94 (20)

 

ESP       EIP      

 

02a0f8fc 7d61cca8   [HelperMethodFrame: 02a0f8fc] System.Threading.Thread.SleepInternal(Int32)

 

02a0f950 0fe90ce8   Link.Finalize()

 

02a0fc1c 79fbcca7   [ContextTransitionFrame: 02a0fc1c]

 

02a0fcec 79fbcca7   [GCFrame: 02a0fcec]

 

Q:什么对象正在被终结?

A:看起来是一个link 对象。

Q:它正在干什么? 为什么这个会导致高内存使用率?

A:终结link对象的终结器线程因为sleep 被堵住了。意味着终结器被堵住,进程中没有东西可以被终结。因而等待终结的进程都会仍然在内存中直到终结器醒来它们被终结为止。

 

 

示例6: 线程状态

!threads命令看看当前CLR中有哪些线程正在执行

以下为引用:

*
0:004> !threads
ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
                             PreEmptive   GC Alloc               Lock
       ID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
  0   6ec 0014e708      6020 Enabled  00000000:00000000 00148a90     0 STA
  2   a68 00157618      b220 Enabled  00000000:00000000 00148a90     0 MTA (Finalizer)*

 

前面5个计数器分别表示托管(managed)线程、未启动线程、后台线程、阻塞线程和僵死线程的数量。
    下面的列表是当前托管线程的详细信息:第一个域是WinDbg的线程编号;ID是Win32线程ID;ThreadObj是线程的对象;State是一个标志位,以后再详细介绍;PreEmptive GC表示GC是否与此线程协作;GC Alloc Context是GC的相关信息;Domain是线程所在AppDomain;Lock Count是线程拥有锁的计数器;APT是线程类型,沿用COM中STA/MTA/NTA(netural)的概念;最后的Exception表示线程类型,除了普通的用户线程外还有finalizer、GC、Theadpool Worker和Threadpool Completion Port,其功能与名字相符.

 

 

示例7: 调试.net代码

 

  1. !name2ee SimpleSample.exe SimpleSample.Program.Main

 

显示方法相关地址

 

0:004> !name2ee SimpleSample.exe SimpleSample.Program.Main

Module: 00982c5c (SimpleSample.exe)

Token: 0x06000005

MethodDesc: 00983000

Name: SimpleSample.Program.Main()

JITTED Code Address: 01220070

 

  1. !dumpil 00983000

显示方法被C#编译器编译之后的IL代码

 

0:004> !dumpil 00983000

ilAddr = 004020c4

IL_0000: nop

IL_0001: ldstr “Any key continue… … “

IL_0006: call System.Console::WriteLine

IL_000b: nop

IL_000c: call System.Console::Read

IL_0011: pop

IL_0012: call SimpleSample.Program::getcharBuffer

IL_0017: stloc.0

IL_0018: ldloc.0

IL_0019: call SimpleSample.Program::changeto4p

IL_001e: nop

IL_001f: ldloc.0

IL_0020: call System.Console::WriteLine

IL_0025: nop

IL_0026: call System.Console::Read

IL_002b: pop

IL_002c: call System.Console::Read

IL_0031: pop

IL_0032: ret

 

  1. !u 01220070

显示JIT编译了的方法的本地代码

 

  Other:

  !dumpmt -md 00983024       //得到类的成员函数详细信息

  !dumpheap -stat              //显示程序中所有对象的统计信息

  !dumpheap -mt 00983024     //该命令显示MethodTable的详细信息

  !gcroot 012919b8            //来显示一个实例的所属关系

  !dumpobj(do) 012a3904      //显示一个对象的具体内容

  !ObjSize 012a1ba4          //对象实际在内存中的大小

  !DumpArray          

     //查看数组信息 () 

 !dumpheap -type Exception   //查看异常信息

 

 

示例8: 查看方法代码

 

!ip2md 05600dfd   –05600dfd: 表示EIP

    MethodDesc: 02429048

    Method Name: DataLayer.GetFeaturedProducts()

    Class: 055b18ac

    MethodTable: 0242905c

    mdToken: 06000008

    Module: 024285cc

    IsJitted: yes

    m_CodeOrIL: 05600dd0

 

I.根据md来看:!dumpil 02429048 (这个地址是上面步骤f中的输出的第一行

MethodDesc的值)

 

II. 根据native code来看:!u 05600dd0
(这个地址是上面步骤f中的输出的最后一行的m_CodeOrIL的值)

 

III.根据module来看:!dumpmodule 024285cc
(这个地址是上面步骤分钟的输出的

倒数第三行的Module的值)

 

 

 

附: WinDbg / SOS Cheat Sheet

 

 

Environment

Attach to   process

F6

Detach from a   process

.detach

Break debugger   execution

Ctrl-Break

Continue   debugger execution

g

Exit WinDbg

q

Clear the screen

.cls

 

Getting   Help

Debugger commands

?

Debugger   commands

.help

Online help file

.hh command

Help on   extension on top of chain

!help

Help on specific   extension command

!help command

 

Issuing   Commands

Scroll through   command history

[up], [down],   [enter]

Paste into   command window

[right-click]

 

Examining   the Unmanaged Environment

List loaded   modules with full path

lmf

List loaded   modules with last modified timestamp

lmt

List unmanaged   threads

~

Select active   thread

~thread_id s

View call stack

k

View thread CPU   consumption

!runaway

Set a breakpoint

bp

Dump small   memory image

.dump path

Dump large   memory image

.dump /ma path

 

Loading   SOS

Load SOS for   .NET 1.x

.load clr10\sos

Load SOS for   .NET 2.0

.loadby sos   mscorwks

 

Examining   the Managed Environment

Dump runtime   type information

!dumpruntimetypes

View managed   threads

!threads

View managed   call stack

!clrstack

View combined   managed / unmanaged callstack

!dumpstack

View function   call arguments

!clrstack –p

View local   variables

!clrstack –l

View object dump

!do address

View array dump

!da address

View object size   (including children)

!objsize address

View heap usage   by type

!dumpheap -stat

View heap usage   filtered by type

!dumpheap -type   type

View GC roots of   object instance

!gcroot address

View managed   sync blocks

!syncblk

View managed   thinlocks (CLR 2.0)

!dumpheap   –thinlock

View information   on most recent exception

!printexception

Set a breakpoint

!bpmd module   method

 

 

Type

Explanation

ESP

ESP=Extended Stack Pointer, Object is in   use on a stack

DOMAIN(001CCE68):HANDLE(Strong)

Strong reference, Typically a static   variable

DOMAIN(001CCE68):HANDLE(WeakLn)

Weak Long Handle, A weak reference that   is tracked through finalization (can be resurrected) 

DOMAIN(001CCE68):HANDLE(WeakSh)

Weak Short Handle, A weak reference,   can’t be resurrected

DOMAIN(001CCE68):HANDLE(Pinned)

Pinned object, pinned at a specific   address, can’t move around during garbage collection.

DOMAIN(001CCE68):HANDLE(RefCnt)

Reference count, referenced as long   as the reference count is > 0.

000007fe`f3703caa :
00000001`5ff73a30 00000002`6f9e4320 00000001`5ff74038
00000001`5ff74040 :
System_Xml_ni!System.Xml.XmlLoader.LoadNode(Boolean)+0xf9

0:000> .loadby sos
clr

第五章 sos.dll 扩展命令

命令

描述

BPMD [<module name>   <method name>] [-md <MethodDesc>]

建立一个断点在指定模块的指定方法上。

如果指定模块和方法尚未被载入,该命令等到该模块被载入并且被即时(just-in-time)编译的通知后再建立断点。

CLRStack [-a] [-l]   [-p]

只提供托管代码的栈跟踪。

-p 选项显示托管函数的参数。

-l 选项显示在一个框架里局部变量的信息。SOS调试扩展无法检索局部变量的名字,所以局部变量的输出格式为<local   address> = <value>。

-a (all) 选项是-l-p组合的快捷方式。

在x64和基于IA-64的平台上,SOS调试扩展不显示过渡框架(Transition Frames)。

COMState

列出每个线程COM单元模型和可用的上下文指针。

DumpArray [-start   <startIndex>] [-length <length>] [-details] [-nofields]   <array object address>

-或者-

DA [-start <startIndex>]   [-length <length>] [-detail] [-nofields] <array   object address>

检查一个数组对象的元素。

-start 选项指定显示元素的起始索引号。

-length 选项指定要显示的元素数目。

-detail 选项按照DumpObjDumpVC格式显示元素的细节。

-nofields 选项使数组显示不包括字段。仅当指定   -detail 选项时该选项才可用。

DumpAssembly <Assembly   address>

显示一个汇编集的有关信息。

如果存在多个模块,DumpAssembly命令将它们全部列出。

你可以用DumpDomain命令得到汇编集地址。

DumpClass <EEClass   address>

显示与一个类型相关的EEClass结构这些信息。

DumpClass命令显示静态字段值而不显示非静态字段值。

使用DumpMTDumpObjName2EE、或Token2EE命令来获取一个EEClass结构地址。

DumpDomain [<Domain   address>]

枚举在指定AppDomain对象地址里面装载的每一个Assembly对象。当不带参数调用DumpDomain命令时,它列出一个进程中所有的AppDomain对象。

DumpHeap [-stat] [-min   <size>][-max <size>] [-thinlock] [-mt   <MethodTable address>] [-type <partial type name>][start   [end]]

显示关于垃圾收集堆的信息和有关对象的收集统计。

DumpHeap命令如果在垃圾收集器堆中检测到过多的碎片,它显示一个警告。

-stat 选项限制输出内容只有统计的类型摘要。

-min 选项忽略那些尺寸小于size参数的对象,以字节为单位。

-max 选项忽略那些尺寸大于size参数的对象,以字节为单位。

-thinlock 选项报告ThinLocks。更多信息请看SyncBlk命令。

-mt 选项只列出符合所指定MethodTable结构的那些对象。

-type 选项只列出类型名字子串匹配指定字符串的那些对象。

参数 start 指定开始列出的地址。

参数 end 指定停止列出的地址。

DumpIL [<DynamicMethod   address>] [<DynamicMethodDesc address>] [<MethodDesc address>]

显示与一个托管方法相关的中间语言(IL)。

注意,动态IL是发射来的(emitted),不同于从一个汇编集装载的IL。动态IL引用一个托管对象数组中的对象,而不是通过元数据标记引用对象。

DumpLog [<Filename>]

把一个内存里的重要日志的内容写入指定文件。如果你没有指定文件名,该命令在当前目录中创建一个名为Stresslog.txt的文件。

公共语言运行时提供一个内存里的重要日志,帮助你诊断重要失败。日志使你可以不使用锁或I/O就能诊断失败。若要启用重要日志,需要在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework下面设置以下注册表项:

(DWORD) StressLog = 1

(DWORD) LogFacility = 0xffffffff

(DWORD) StressLogSize = 65536

DumpMD <MethodDesc   address>

显示的信息是在指定地址上的一个MethodDesc结构。

你可以用IP2MD命令得到一个托管函数的MethodDesc结构地址。

DumpMT [-MD]   <MethodTable address>

显示在指定地址上的一个方法表的有关信息。指定 -MD   选项显示列出该对象定义的所有方法。

每个托管对象包含有一个方法表指针。

DumpMethodSig <sigaddr>   <moduleaddr>

显示在指定地址上的一个MethodSig结构的有关信息。

DumpModule [-mt]   <Module address>

显示在指定地址上的一个模块的有关信息。-mt 选项显示在该模块中所定义的类型和被该模块引用的类型。

你可以用DumpDomainDumpAssembly命令检索一个模块的地址。

DumpObj <object   address>

-或者-

DO <object address>

显示在指定地址上的一个对象的有关信息。DumpObj命令显示字段、EEClass结构信息、方法表和该对象的尺寸。

你可以用DumpStackObjects命令检索一个对象的地址。

注意,因为类型CLASS的字段也是对象,所以你可以对它们执行DumpObj命令。

DumpRuntimeTypes

显示在垃圾收集器堆中的运行时类型对象,并列出与它们相关的类型名字和方法表。

DumpStack [-EE]   [top stack [bottom stack]]

显示一个栈跟踪(回溯)。

-EE 选项使DumpStack命令只显示托管函数。在x86平台上使用topbottom参数限制所显示的栈框架。

在x86平台上,DumpStack命令创建一个冗长的栈跟踪。

在x64和基于IA-64的平台上,DumpStack命令模仿调试器的 K 命令。在x64和基于IA-64的平台上topbottom参数被忽略。

DumpSig <sigaddr>   <moduleaddr>

显示在指定地址上的一个Sig结构的有关信息。

DumpStackObjects [-verify]   [top stack [bottom stack]]

-或者-

DSO [-verify] [top   stack [bottom stack]]

显示在当前栈范围内找到的所有托管对象。

-verify 选项验证对象字段的每一个非静态CLASS字段。

带有栈跟踪命令使用DumpStackObject命令,比如 K 命令和CLRStack命令确定局部变量和参数的值。

DumpVC <MethodTable   address> <Address>

显示在指定地址上的一个值类的字段信息。

MethodTable参数使DumpVC命令能够正确地解释字段。值类不以方法表作为它们的第一个字段。

EEHeap [-gc] [-loader]

显示被公共语言运行时内部数据结构使用的进程内存的有关信息。

-gc-loader 选项限制该命令的输出内容为垃圾收集器或者装载器的数据结构。

对于垃圾收集器,列出在托管堆里每一个节的范围信息。如果某指针是在EEHeap -gc给出的某个节范围内,那么该指针是一个对象指针。

EEStack [-short] [-EE]

对进程中所有线程执行DumpStack命令。

-EE 选项被直接传递给DumpStack命令。-short 参数限制输入内容为以下线程种类:

  1. 已经被锁定的线程。
  2. 为了允许垃圾收集已经被迟延的线程。
  3. 目前处于托管代码中的线程。

EEVersion

显示公共语言运行时版本。

EHInfo [<MethodDesc   address>] [<Code address>]

显示所指定方法里的异常处理块。这个命令显示子句块(try块)和处理者块(catch块)的代码地址及偏移量。

FinalizeQueue [-detail]

显示为终结(finalization)而登记的所有对象。

-detail 选项显示关于等待清除的任何SyncBlocks的附加信息和等待清除的任何RuntimeCallableWrappers (RCWs) 的额外信息。两个数据结构都是由终结器(finalizer)线程缓存和清除。

FindAppDomain <Object   address>

确定在指定地址上的一个对象的应用程序域。

GCHandles [-perdomain]

显示在进程中垃圾收集器句柄的统计。

如果传递-perdomain 选项,则按照应用程序域顺序排列统计。

使用GCHandles命令查找由垃圾收集器句柄泄漏引起的内存泄漏。例如,由于一个强健的垃圾收集器句柄指向代码的一个大数组成部分,而该句柄没有被释放就丢弃了,所以代码实际上还保留着这个数组,这时就出现一个内存泄漏。

GCHandleLeaks

在内存里搜索进程中对那些强健而且有麻烦的垃圾收集器句柄的任何引用,并且显示结果。如果找到某个句柄,GCHandleLeaks命令显示该引用的地址。如果在内存里没有找到某个句柄,这个命令显示一个通知。

GCInfo <MethodDesc   address><Code address>

显示数据指示何时寄存器或栈位置包含有托管对象。如果发生垃圾收集,收集器必须知道指向对象的引用的位置,如此它才可以用新的对象指针值更新它们。

GCRoot [-nostacks]   <Object address>

显示对在指定地址上的一个对象的引用(或根)信息。

GCRoot命令检查整个托管堆和在栈以及其他对象里面句柄的句柄表。然后,在每个栈和终结器队列中搜索指向对象的指针。

这个命令不确定一个栈根是有效的还是已丢弃的。为了确定栈根是否还在使用中,需要用CLRStackU命令反汇编局部变量或参数值所属的框架。

-nostacks 选项限制只搜索垃圾收集器句柄和终结器队列里的对象(freachable objects)。

help [<command>]   [<faq>]

当没有指定参数时显示所有可用命令,或者当指定命令为参数时显示其详细帮助信息。

faq 参数显示常问问题的答案。

IP2MD <Code address>

显示在已经即时编译(JIT)的代码里指定地址上的MethodDesc结构。

MinidumpMode [0] [1]

防止在使用一个小转储(minidump)时执行非安全命令。

传递 0 以禁用这个功能,或传递 1 以启用这个功能。默认地,MinidumpMode把值设置为 0

.dump /m 命令或者 .dump 命令创建的小转储已经限制为特定的CLR数据,并且让你只可以正确地运行SOS命令的一个子集。有些命令可能因不可预见的错误而失败,因为所必需的内存区域没有被映射或者只有部分被映射。这个选项让你避免对小转储执行非安全命令。

Name2EE <module   name> <type or method name>

-或者-

Name2EE <module   name>!<type or method name>

显示指定模块中指定类型或方法的MethodTable结构和EEClass结构。

指定模块必须被装入进程中。

可以使用MSIL反汇编器 (Ildasm.exe) 浏览模块,以取得适当的类型名字。你也可以传递 * 作为模块名字参数以搜索所有装入的托管模块。模块名字参数也可以是调试器给一个模块的名字,比如mscorlibimage00400000

这个命令支持Windows调试器句法<module>!<type>。该类型必须被完全限定。

ObjSize [<Object   address>]

显示指定对象的尺寸。若不带参数,则ObjSize命令显示在托管线程中找到的全部对象的尺寸,显示进程中全部的垃圾收集器句柄,并求出指向那些句柄的所有对象的尺寸总和。ObjSize命令把父对象全部子对象的尺寸也计算在内。

PrintException [-nested]   [<Exception object address>]

-或者-

PE [-nested]   [<Exception object address>]

编排格式并显示在指定地址上的任何Exception类派生对象的字段。如果你没有指定一个地址,PrintException命令显示当前线程上最近抛出的异常。

-nested 选项详细显示嵌套的异常对象。

你可以使用这个命令编排格式并查看_stackTrace字段,这是一个二元数组。

ProcInfo [-env] [-time]   [-mem]

显示针对该进程的环境变量、内核CPU时间和内存使用统计。

RCWCleanupList   <RCWCleanupList address>

显示在指定地址上的正等待清除的运行时可调用的包裹器列表。

SaveModule <Base   address> <Filename>

把装入在指定地址上的一个内存映像写入指定文件。

StopOnException [-derived]   [-create | -create2] <Exception> <Pseudo-register   number>

使调试器当指定异常被抛出时停止,而当其他异常被抛出时则继续运行。

-derived 选项捕获指定异常及其衍生的每个异常。

SyncBlk [-all |   <syncblk number>]

显示指定的SyncBlock结构或者所有的SyncBlock结构。如果你没有传递任何参数,SyncBlk命令显示一个线程所有对象相应的SyncBlock结构。

一个SyncBlock结构是一个附加信息的容器,不必为每个对象创建它。它能够容纳COM互用数据、散列码、和用于线程-安全操作的锁定信息。

ThreadPool

显示托管线程池的有关信息,包括在队列中工作请求的数目、完全端口线程的数目、和计时器数目。

Token2EE <module name>   <token>

把指定模块中指定的元数据标记转换成一个MethodTable结构或者MethodDesc结构。

你也可以把 * 作为模块名字参数,以使在每个被载入的托管模块中找出该标记的映射目标。模块名字参数也可以是调试器给一个模块的名字,比如 mscorlib image00400000

Threads [-live] [-special]

显示进程中所有的托管线程。

Threads命令显示 调试器简写ID号、公共语言运行时线程ID号、和正在操作中的系统线程ID号。此外,Threads命令显示 一个Domain栏指示线程运行所处在的应用程序域、一个APT栏显示COM单元的模式、和一个Exception栏显示线程最近抛出的异常。

-live 选项显示与某个活线程有关联的那些线程。

-special 选项显示CLR创建的所有特别线程。特别线程包括(并发GC和服务器GC中的)垃圾收集(GC)线程、调试器助手线程、Finalizer线程、AppDomain卸载线程、和线程池计时器线程。

TraverseHeap [-xml]   <filename>

遵照CLR简档器隐含的格式把堆信息写入到指定文件。-xml选项使TraverseHeap命令把该文件格式化为XML。

你能够从: http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda   下载CLR简档器。

U [-gcinfo] [-ehinfo]   <MethodDesc address> | <Code address>

通过指定一个指向某个方法MethodDesc结构的指针或者指定其方法体里面的一个代码地址,显示一个托管方法有注释的反汇编。U命令显示整个方法,从开始到完成,并在注释里把元数据标记转换为名字。

-gcinfo 选项使U命令显示这个方法使用的GCInfo结构。

-ehinfo 选项显示这个方法的异常信息。你也可以用EHInfo命令来获取该信息。

VerifyHeap

检查垃圾收集器堆的崩溃标志,显示发现的任何错误。

堆崩溃能够由不正确地构成的平台援用(platform invoke)调用引起。

VMMap

横跨虚拟地址空间,显示加诸每区域的保护类型。

VMStat

按照加诸内存的保护类型(自由的free、保留的reserved、约束的committed、私有的private、映射的mapped、映像image)顺序,提供虚拟地址空间的概览。TOTAL栏显示AVERAGE栏乘以BLK   COUNT栏的结果。

 

 注:个人总结整理,如有指教问题请liudaoyu@outlook.com 谢谢!

 

 

000007fe`f37036e0 :
00000001`5ff74038 00000001`5ff74040 00000001`5ff738e8
00000000`0d37b670 :
System_Xml_ni!System.Xml.XmlLoader.LoadDocSequence(System.Xml.XmlDocument)+0x4a

0:000> !pe

000007fe`f370308d :
00000001`5ff738e8 00000002`7f992078 00000001`5ff738e8
00000000`0d37b6f0 :
System_Xml_ni!System.Xml.XmlDocument.Load(System.Xml.XmlReader)+0x90

Exception object:
744a8e84

000007ff`004007f6 :
00000000`23a3db85 000007fe`f71f1590 00000000`00000000
00000000`00000000 :
System_Xml_ni!System.Xml.XmlDocument.Load(System.String)+0xcd

Exception type:  
System.OutOfMemoryException

000007ff`0040039c :
00000001`5ff732b0 00000002`6f9e4320 00000001`8f98ee20
80000000`00000001 :
XXXXXX_Sql_Common!Common.XmlHelper.InsertNode(System.String,
System.String, Boolean, System.String, System.Collections.Hashtable,
System.Collections.Hashtable)+0xb6

Message:         
<none>

000007ff`00400060 :
00000002`0f980e00 00000001`5ff72c48 00000001`3f98b328
000007fe`f8266279 : XXXXXX_Sql_Common!Common.ErrorLog.Insert(System.String,
System.String, System.String)+0x30c

InnerException:  
<none>

000007ff`004defbc :
00000002`0f980e00 00000001`5ff72ba8 00000001`3f98b328
00000000`0d37c2c0 :
XXXXXX_Sql_Common!Common.ErrorLog.Insert(System.String,
System.Exception, System.String)+0x60

StackTrace
(generated):

000007fe`f82665ed :
00000000`0e0936d0 00000000`0e0936d0 000007ff`004def74
00000000`0d37c370 :
XXXXXX_Web!EasyMail.Web.login.Page_Load(System.Object,
System.EventArgs)+0x19ec

SP       IP      
Function

000007fe`f82663f2 :
00000000`0377e3a0 000007ff`004def74 00000000`0d37dea0
000007ff`004cbe98 : clr!ExceptionTracker::CallHandler+0x1c1

#

000007fe`f8266342 :
00000000`0d37dea0 00000000`0d37bc80 00000000`0377e3a0
00000000`0d37dea0 : clr!ExceptionTracker::CallCatchHandler+0x7e

# 不出所料,StreamReader里面使用StringBuilder将文本文件读入到一个字符串里。而StringBuilder是动态

00000000`76ec9d8d :
000007ff`004de400 00000000`0d37dea0 00000000`0d37c2c0
00000000`0d37bc80 : clr!ProcessCLRException+0x2d6

# 分配内存的。

00000000`76eb8a2c :
00000000`0d380000 00000000`0d37c2c0 00000206`0005af30
000007fe`f81e9b05 : ntdll!RtlpExecuteHandlerForUnwind+0xd

#

000007fe`f82686a0 :
00000000`0d37dea0 00000000`0000bad0 00000000`0d37d960
00000000`0e0936d0 : ntdll!RtlUnwindEx+0x539

    0017ED38
5713E61E mscorlib_ni!System.Text.StringBuilder.ToString()+0x1e

000007fe`f826865c :
00000000`00000000 00000000`0377e3a0 00000000`0d37c940
00000000`0d37dea0 : clr!ClrUnwindEx+0x40

    0017ED64
57121991 mscorlib_ni!System.IO.StreamReader.ReadToEnd()+0x7d

00000000`76ec9d0d :
000007ff`004de481 00000000`0d37dea0 00000000`0d37d330
00000000`0d37c940 : clr!ProcessCLRException+0x2a0

    0017ED78
002F0136
ConsoleApplication1!ConsoleApplication1.Program.Main(System.String[])+0xc6

00000000`76eb91af :
00000000`0d380000 000007fe`cd224604 00000000`0005af30
00000000`036ece50 : ntdll!RtlpExecuteHandlerForException+0xd

 

00000000`76eb97a8
: 00000000`0d37d960 00000000`0d37d330 00000000`00000001
00000000`00000000 : ntdll!RtlDispatchException+0x45a

StackTraceString:
<none>

000007fe`fd0b9e5d
: 00000000`0e0936d0 00000000`00000001 00000000`00000001
00000000`0e0936d0 : ntdll!RtlRaiseException+0x22f

HResult:
8007000e

000007fe`f8266d1d
: 00000000`0e0936d0 00000000`00000001 00000000`0d37dbf1
00000001`5ff72ba8 : KERNELBASE!RaiseException+0x39

#

000007fe`f826f91e
: 00000000`0d37dbd8 00000001`5ff72ba8 00000000`0e0936d0
00000001`5ff72ba8 : clr!RaiseTheExceptionInternalOnly+0x363

# 使用AnalyzeOOM来分析一下原因,看看是GC哪一个内存区域导致了这个异常。

000007fe`f826f76b
: 00000000`ffb07948 00000000`00000001 00000000`0e0936d0
00000000`0e0936d0 : clr!Thread::UserAbort+0x19a

#

000007fe`f71b5929
: 00000002`7f992078 00000001`5ff72b90 00000000`00000000
00000000`00000000 : clr!ThreadNative::Abort+0x14b

0:000> !ao

000007fe`cd19b9a6
: 00000000`ffb45ad0 00000001`dfe872a8 00000001`8f98edc0
00000000`00000000 :
mscorlib_ni!System.Threading.Thread.Abort(System.Object)+0x39

#

000007ff`004de481
: 00000002`0f980da0 00000001`dfe86a90 00000001`3f98b328
80000000`00000000 :
System_Web_ni!System.Web.HttpResponse.End()+0x166

# 找到了,是大对象堆(Large Object Heap –
LOH),GC在尝试申请一个1.5G的内存空间时

000007fe`fad79c09 :
00000001`ef9f1b18 00000001`ef9f1b18 00000001`3f98add0
00000000`0d37e680 :
XXXXXX_Web!XXXXXX.Web.login.Page_Load(System.Object,
System.EventArgs)+0xeb1

# 遭拒。

000007fe`cd1b9217 :
00000001`efa2bfb8 000007fe`cd19e96b 00000000`00000000
00000000`00000000 :
System_Web_RegularExpressions_ni!System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr,
System.Object, System.Object, System.EventArgs)+0x19

#

000007fe`cd1b4d38 :
00000001`efa01d90 00000000`ffb68778 00000000`ffb68501
000007fe`cd19ad55 :
System_Web_ni!System.Web.UI.Control.LoadRecursive()+0x47

Managed OOM
occured after GC #312 (Requested to allocate 1649667816 bytes)

000007fe`cd1b4011 :
00000001`ef9f1b18 000007fe`cd1b6601 00000000`00000001
000007fe`cd1a4f38 :
System_Web_ni!System.Web.UI.Page.ProcessRequestMain(Boolean,
Boolean)+0xbf8

Reason:
Didn’t have enough memory to allocate an LOH segment

000007fe`cd1b3ebd :
00000001`ef9f1b18 00000002`7f9980a0 00000000`ffb68501
00000000`00000004 :
System_Web_ni!System.Web.UI.Page.ProcessRequest(Boolean,
Boolean)+0xc1

Detail: LOH:
Failed to reserve memory (1652555776 bytes)

000007fe`cd1b2cb7 :
00000001`ef9f1b18 000007fe`cd19fc2a 000007ff`00143cf0
000007fe`cd1bc313 :
System_Web_ni!System.Web.UI.Page.ProcessRequest()+0x10d

#

000007ff`004dc5e3 :
00000000`0d37e850 00000001`efa01d90 00000000`ffb683c0
00000000`0d37e8d0 :
System_Web_ni!System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)+0xa7

# 使用!eeheap
–gc命令找到GC里,各个generation的堆的起始地址和结束地址

000007fe`cd1bc7a1 :
00000001`ef9f1b18 00000000`ffb45810 00000001`3fa5bb88
00000000`00000000 :
App_Web_0khihdrb!ASP.login_aspx.ProcessRequest(System.Web.HttpContext)+0x33

#

000007fe`cd1891fe :
00000000`ff9c0001 00000000`ffa11980 00000000`ffb683c0
00000000`00000001 :
System_Web_ni!System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()+0x271

0:000> !eeheap -gc

000007fe`cd19d49a :
00000001`3fa59cc8 00000001`3fa5bc50 00000000`0d37eab0
000007fe`cd29fd68 :
System_Web_ni!System.Web.HttpApplication.ExecuteStep(IExecutionStep,
Boolean ByRef)+0x10e

Number of GC
Heaps: 1

000007fe`cd1886e1 :
00000001`3fa5b9a0 00000000`00000000 00000002`7f9980a0
00000000`0d37ebb0 :
System_Web_ni!System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)+0x20a

generation 0
starts at 0x744a8e78

000007fe`cd18d6ae :
01ce7e07`d83d1609 00000000`ffa6e3b8 00000000`00000000
00000000`00000004 :
System_Web_ni!System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext,
System.AsyncCallback, System.Object)+0xf1

generation 1
starts at 0x7446a408

000007fe`cd18c377 :
00000000`ff9bf918 00000000`ffb44b30 00000000`0d37ebf0
00000000`00000000 :
System_Web_ni!System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)+0x26e

generation 2
starts at 0x01911000

000007fe`cd90f80d :
00000000`ffa68ed8 00000000`00000000 000007fe`f71ab630
000007fe`f87ce8b0 :
System_Web_ni!System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr,
Int32)+0x1c7

ephemeral segment
allocation context: none

000007fe`f8249b9e :
00000000`0124fda0 00000000`01338310 00000000`00000002
00000000`0d37f330 :
System_Web_ni!DomainNeutralILStubClass.IL_STUB_COMtoCLR(Int64,
Int32, Int32 ByRef)+0x1d

 segment     begin
allocated size

000007fe`f8249782 :
00000000`0383a7d0 000007fe`f81e8244 00000000`0d37f0a8
00000000`00000000 : clr!COMToCLRDispatchHelper+0x4e

01910000 01911000 0290aee0 0xff9ee0(16752352)

000007fe`f8249686 :
00000000`00000000 000007fe`cd11a308 00000000`ffa68ed8
00000000`0d37f2d0 : clr!COMToCLRWorkerBody+0x1ca

03fd0000 03fd1000 04fcd3ec 0xffc3ec(16761836)

000007fe`f82495a2 :
00000000`00000002 00000000`00000000 00000000`0d37f3f0
00000000`00000000 : clr!COMToCLRWorkerBody+0xd6

… …

000007fe`f8249e85 :
00000000`00000000 00000000`037733f0 00000000`00000002
00000000`00000000 : clr!COMToCLRWorkerDebuggerWrapper+0x22

73ca0000 73ca1000 744aae84 0x809e84(8429188)

000007fe`f8249b47 :
00000000`0e0936d0 00000000`0d37f2d0 00000000`012d8400
00000000`00000001 : clr!COMToCLRWorker+0x44b

Large object heap
starts at 0x02911000

000007fe`e1f122d6 :
00000000`0124fda0 00000000`01338310 00000000`00000002
00000000`0d37f330 : clr!GenericComCallStub+0x57

#

000007fe`e1f12e06 :
00000000`00000001 00000000`0e91aa10 00000000`00000000
00000000`0d37f950 :
webengine4!HttpCompletion::ProcessRequestInManagedCode+0x1e8

#
LOH的起始地址找到了,0x02911000就是LOH的起始地址

000007fe`e1f12dc3 :
00000000`01338310 00000000`00000000 ffffffff`fffffffe
00000000`00000000 :
webengine4!HttpCompletion::ProcessCompletion+0x5c

# 已经分配了0x02913240个字节,所以结束地址就是

000007fe`f82413d5 :
000007fe`e1f12da0 00000000`0d37f870 00000000`00000000
000007fe`f89cdfa0 :
webengine4!CorThreadPoolWorkitemCallback+0x24

# 0x02911000 +
0x02913240

000007fe`f8240908 :
00000000`0e0936d0 00000000`00000001 00000000`00000000
00000000`d73646db :
clr!UnManagedPerAppDomainTPCount::DispatchWorkItem+0x181

#

000007fe`f834b7bf :
00000000`00000001 00000000`0ecd3c01 00000002`00000804
00000003`001a001a :
clr!ThreadpoolMgr::NewWorkerThreadStart+0x2e5

 segment     begin
allocated size

000007fe`f8349b56 :
00000000`00000000 00000000`00000000 00000000`00000000
00000000`00000000 : clr!ThreadpoolMgr::WorkerThreadStart+0x3b

02910000 02911000 02913240 0x2240(8768)

        00000000`76d9652d :
00000000`00000000 00000000`00000000 00000000`00000000
00000000`00000000 : clr!Thread::intermediateThreadProc+0x7d

Total
Size:              Size: 0x629424c0 (1653875904) bytes.

        00000000`76ecc521 :
00000000`00000000 00000000`00000000 00000000`00000000
00000000`00000000 : kernel32!BaseThreadInitThunk+0xd


00000000`00000000 :
00000000`00000000 00000000`00000000 00000000`00000000
00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

GC Heap
Size:            Size: 0x629424c0 (1653875904) bytes.

在堆栈中,以上标红的部分有一个Thread.Abort()
调用,这会引发TheadAbortException 异常的抛出。

#

2) 提取异常对象进行查看:

# 使用dumpheap看一下LOH当中各个对象的内存分配情况。

0:071> !do 00000001`5ff72ba8

Name:        System.Threading.ThreadAbortException

MethodTable: 000007fef72b6ef0

EEClass:     000007fef6df1860

Size:        160(0xa0) bytes

File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

Fields:

              MT    Field   Offset                 Type VT     Attr            Value Name

000007fef72b6738  400004b        8        System.String  0 instance 0000000000000000 _className

000007fef72b7760  400004c       10 …ection.MethodBase  0 instance 0000000000000000 _exceptionMethod

000007fef72b6738  400004d       18        System.String  0 instance 0000000000000000 _exceptionMethodString

000007fef72b6738  400004e       20        System.String  0 instance 000000015ff72c48 _message

000007fef72d9bc8  400004f       28 …tions.IDictionary  0 instance 0000000000000000 _data

000007fef72b6b00  4000050       30     System.Exception  0 instance 0000000000000000 _innerException

000007fef72b6738  4000051       38        System.String  0 instance 0000000000000000 _helpURL

000007fef72b5890  4000052       40        System.Object  0 instance 000000015ff72d50 _stackTrace

000007fef72b5890  4000053       48        System.Object  0 instance 0000000000000000 _watsonBuckets

000007fef72b6738  4000054       50        System.String  0 instance 0000000000000000 _stackTraceString

000007fef72b6738  4000055       58        System.String  0 instance 0000000000000000 _remoteStackTraceString

000007fef72bc620  4000056       88         System.Int32  1 instance                0 _remoteStackIndex

000007fef72b5890  4000057       60        System.Object  0 instance 0000000000000000 _dynamicMethods

000007fef72bc620  4000058       8c         System.Int32  1 instance      -2146233040 _HResult

000007fef72b6738  4000059       68        System.String  0 instance 0000000000000000 _source

000007fef72c3318  400005a       78        System.IntPtr  1 instance                0 _xptrs

000007fef72bc620  400005b       90         System.Int32  1 instance       -532462766 _xcode

000007fef72b6248  400005c       80       System.UIntPtr  1 instance      7fef71b5929 _ipForWatsonBuckets

000007fef72eb600  400005d       70 …ializationManager  0 instance 000000015ff72c70 _safeSerializationManager

0:071> !do 000000015ff72c48

Name:        System.String

MethodTable: 000007fef72b6738

EEClass:     000007fef6e3ed68

Size:        40(0x28) bytes

File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

String:      正在中止线程。

Fields:

              MT    Field   Offset                 Type VT     Attr            Value Name

000007fef72bc620  4000103        8         System.Int32  1 instance                7 m_stringLength

000007fef72bb160  4000104        c          System.Char  1 instance             6b63 m_firstChar

000007fef72b6738  4000105       10        System.String  0   shared           static Empty

                                 >> Domain:Value  00000000037733f0:000000015f980488 000000000383a7d0:000000015f980488 <<

#

3)
根据堆栈显示,异常对象抛出之后,被程序代码捕获并通过XXXXXX_Sql_Common!Common.ErrorLog.Insert()
记录下来:

#
dumpheap的参数中,0x02911000是要查看的内存的起始地址,

000007fe`f398d1c8 : 00000001`5ff74050 00000000`00001296 00000001`5ff76c08 00000000`00000000 : clr!Buffer::BlockCopy+0x1a5

000007fe`f36b932a : 00006dae`d26d74e7 000007fe`f87cdfc0 00000000`0d37dce8 00000000`00000000 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ShiftBuffer(Int32, Int32, Int32)+0x28

000007fe`f36b8c6d : 00000000`0d37b4d0 00000001`5ff76c08 00000014`00054cfe 000007fe`f718f13e : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseText(Int32 ByRef, Int32 ByRef, Int32 ByRef)+0x19a

000007fe`f36b8a87 : 00000002`3067ffe0 000007fe`f36c0cdb 000007fe`f37578c0 00000002`30680008 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseText()+0x6d

000007fe`f36c02f9 : 00000001`5ff740b0 000007fe`f36bfd6d 00000001`5ff74060 00000001`5ff74050 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseElementContent()+0x27

000007fe`f3703caa : 00000001`5ff73a30 00000002`6f9e4320 00000001`5ff74038 00000001`5ff74040 : System_Xml_ni!System.Xml.XmlLoader.LoadNode(Boolean)+0xf9

000007fe`f37036e0 : 00000001`5ff74038 00000001`5ff74040 00000001`5ff738e8 00000000`0d37b670 : System_Xml_ni!System.Xml.XmlLoader.LoadDocSequence(System.Xml.XmlDocument)+0x4a

000007fe`f370308d : 00000001`5ff738e8 00000002`7f992078 00000001`5ff738e8 00000000`0d37b6f0 : System_Xml_ni!System.Xml.XmlDocument.Load(System.Xml.XmlReader)+0x90

000007ff`004007f6 : 00000000`23a3db85 000007fe`f71f1590 00000000`00000000 00000000`00000000 : System_Xml_ni!System.Xml.XmlDocument.Load(System.String)+0xcd

000007ff`0040039c : 00000001`5ff732b0 00000002`6f9e4320 00000001`8f98ee20 80000000`00000001 : EasyMail_Sql_Common!Common.XmlHelper.InsertNode(System.String, System.String, Boolean, System.String, System.Collections.Hashtable, System.Collections.Hashtable)+0xb6

000007ff`00400060 : 00000002`0f980e00 00000001`5ff72c48 00000001`3f98b328 000007fe`f8266279 : EasyMail_Sql_Common!Common.ErrorLog.Insert(System.String, System.String, System.String)+0x30c

000007ff`004defbc : 00000002`0f980e00 00000001`5ff72ba8 00000001`3f98b328 00000000`0d37c2c0 : EasyMail_Sql_Common!Common.ErrorLog.Insert(System.String, System.Exception, System.String)+0x60

000007fe`f82665ed : 00000000`0e0936d0 00000000`0e0936d0 000007ff`004def74 00000000`0d37c370 : EasyMail_Web!EasyMail.Web.login.Page_Load(System.Object, System.EventArgs)+0x19ec

000007fe`f82663f2 : 00000000`0377e3a0 000007ff`004def74 00000000`0d37dea0 000007ff`004cbe98 : clr!ExceptionTracker::CallHandler+0x1c1

# 而0x02911000 +
0x02913240是查看的结束地址

4) 通过进一步检查,发现记录日志的文件在D:\XXXX\SysLog\ErrorLog.XML

#

0:071> !do 00000002`6f9e4320

Name:        System.String

MethodTable: 000007fef72b6738

EEClass:     000007fef6e3ed68

Size:        86(0x56) bytes

File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

String:      D:\XXXX\SysLog\ErrorLog.XML

Fields:

              MT    Field   Offset                 Type VT     Attr            Value Name

000007fef72bc620  4000103        8         System.Int32  1 instance               30 m_stringLength

000007fef72bb160  4000104        c          System.Char  1 instance               44 m_firstChar

000007fef72b6738  4000105       10        System.String  0   shared           static Empty

                                 >> Domain:Value  00000000037733f0:000000015f980488 000000000383a7d0:000000015f980488 <<

0:000> !dumpheap -stat
02911000 02911000+02913240

原因分析:

total 0
objects

通过以上分析步骤,可以了解到代码的执行流程大致如下:

Statistics:

a) XXXXXX_Web!XXXXXX.Web.login.Page_Load() 在执行HttpWebResponse.End()
时抛出了ThreadAbortException 异常;

      MT   
Count    TotalSize Class Name

b) 该异常抛出后被程序代码捕获;

00529748       
4           84      Free

c) 捕获的异常信息通过XXXXXX_Sql_Common!Common.ErrorLog.Insert()
方法写入到位于D:\XXXX\SysLog\ErrorLog.XML 的文件中;

57166c28       
3         8720 System.Object[]

首先,页面在执行代码时抛出异常就是不正确的。通过查询KB
了解到,这是一个已知问题:

#

# 不出所料,StringBuilder和Char[]对象最多,但是

KB 中已经给出了不抛出异常的方法,即使用Response.Redirect()
方法的一个重载:

#
Char[]的数组大小有171M之多。

Response.Redirect(url, false);

#

其次,通过提取服务器上的日志文件,我们发现这个文件较大,约15MB。通过实际编写程序测试,在这个文件中插入一个异常信息,耗时较多:

571afb78    
1119        31332 System.Text.StringBuilder

这个15MB 的XML 文件在打开,保存是耗时较长。

571b1d88     1120     17933440
System.Char[]

结论:

Total 2246
objects

由于登录页面在SSO
登录时采用了不恰当的页面跳转方法,导致页面抛出ThreadAbortException
异常,并引发页面打开一个体积较大的XML
文件进行读写。最终导致了页面跳转非常缓慢的现象。尤其是在多人同时访问时,导致了对XML
日志文件的争用。导致用户并发访问量越大,跳转速度越慢。

#

修复建议:

# 使用!dumpheap的-mt参数看看char[]数组的详细内存分配情况

1) 使用Respons.Redirect(uri, false); 实现页面跳转。

#

 

#
571b1d88这个参数,来自于前面的!dumpheap命令的输出(注意高亮显示的地方)

原文来自:

# 这个参数告诉dumpheap命令,只检索char[]数组(Method
table是571b1d88)

 

# 的情况。

#

0:000> !dumpheap -mt 571b1d88 02911000
02911000+02913240

 Address      
MT     Size

03fd1000 571b1d88    16012    

… …

05222c90
571b1d88    16012    

total 0
objects

Statistics:

      MT   
Count    TotalSize Class Name

571b1d88    
1120     17933440 System.Char[]

Total 1120
objects

#

# 随便选一个对象(注意上面一个高亮显示的文本),看看它到底被谁引用了,

# 导致GC一直不释放它,毕竟我电脑的有2G的物理内存,读取几百兆的文件,

# 就触发了OutOfMemoryException,的确有点离谱。

#

0:000> !gcroot 03fd1000

Note: Roots found
on stacks may be false positives. Run “!help gcroot” for

more info.

Scan Thread 0
OSTHread 1688

ESP:17eca4:Root: 0191d2e0(System.Text.StringBuilder)->

 744a4fd0(System.Text.StringBuilder)->

 … …

 5de44814(System.Text.StringBuilder)->

Command cancelled
at the user’s request.

 03fd1000(System.Char[])

#

# 再看看0x0191d2e0这个StringBuilder对象都在哪些地方被引用到了,

# 根据GC的实现,堆栈上各个函数的局部变量是当作root来处理的

#

0:000> !gcroot 0191d2e0

Note: Roots found
on stacks may be false positives. Run “!help gcroot” for

more info.

Scan Thread 0
OSTHread 1688

ESP:17eca4:Root: 0191d2e0(System.Text.StringBuilder)

ESP:17eca8:Root: 0191d2e0(System.Text.StringBuilder)

ESP:17ecb4:Root: 0191d2e0(System.Text.StringBuilder)

ESP:17ecbc:Root: 0191d2e0(System.Text.StringBuilder)

ESP:17ed3c:Root: 0191d2e0(System.Text.StringBuilder)

ESP:17ed58:Root: 0191d2e0(System.Text.StringBuilder)

Scan Thread 2
OSTHread ce8

#

# 从前面的输出,随便找两个对象(前面标注的文本),看看它们都是哪些函数的局部变量

# 例如17ed58这个地址介于System.IO.StreamReader.ReadToEnd()和

#
System.Text.StringBuilder.ToString()之间,也就是StreamReader.ReadToEnd()

# 这个函数定义的,至此,基本上我们可以认为已经找到影响性能的元凶了。

#

0:000> !dumpstack

OS Thread Id:
0x1688 (0)

Current frame:
KERNELBASE!RaiseException+0x54

ChildEBP
RetAddr Caller, Callee

0017eb7c 753b9617
KERNELBASE!RaiseException+0x54, calling ntdll!RtlRaiseException

0017eba0 5888bc5e
clr!DllUnregisterServerInternal+0x15c62, calling
clr!DllUnregisterServerInternal+0xa55b

0017ebac 588fa99c
clr!LogHelp_TerminateOnAssert+0x2df44, calling
clr!DllUnregisterServerInternal+0x15c45

0017ebbc 58871bd0
clr+0x1bd0, calling clr+0x1b8b

0017ebc4 588fac08
clr!LogHelp_TerminateOnAssert+0x2e1b0, calling
KERNEL32!RaiseException

0017ec54 5896ab0b
clr!CopyPDBs+0x4abd, calling
clr!LogHelp_TerminateOnAssert+0x2e03e

0017ec90 58b27c9e
clr!CorLaunchApplication+0x11756, calling clr!CopyPDBs+0x4a78

0017ecdc 588908f6
clr!CoUninitializeEE+0x272e, calling
clr!GetMetaDataInternalInterface+0xde18

0017ed30
5713e61e (MethodDesc 56f0c09c +0x1e
System.Text.StringBuilder.ToString()), calling 00242350

0017ed5c
57121991 (MethodDesc 56efff40 +0x7d
System.IO.StreamReader.ReadToEnd())

0017ed70 002f0136
(MethodDesc 00253840 +0xc6
ConsoleApplication1.Program.Main(System.String[]))

0017edd4 588721db
clr+0x21db

0017ede4 58894a2a
clr!CoUninitializeEE+0x6862, calling clr+0x21a8

… …

0017f91c 589daf00
clr!CorExeMain+0x1c, calling clr!GetCLRFunction+0xd6a

0017f954 718455ab
mscoreei!CorExeMain+0x38

0017f960 6f667f16
MSCOREE!CreateConfigStream+0x13f

0017f970 6f664de3
MSCOREE!CorExeMain+0x8, calling MSCOREE!CorExeMain+0x2f14

0017f978 75d41194
KERNEL32!BaseThreadInitThunk+0x12

0017f984 7723b495
ntdll!RtlInitializeExceptionChain+0x63

0017f9c4
7723b468 ntdll!RtlInitializeExceptionChain+0x36, calling
ntdll!RtlInitializeExceptionChain+0x3c

相关文章

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注