第一章 准备
1.1. 环境配置
_NT_DEBUGGER_EXTENSION_PATH=C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
_NT_SYMBOL_PATH=SRV*c:\Symbols*http://msdl.microsoft.com/download/symbols
Path add:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
C:\Program Files\Debugging Tools for Windows (x86)
1.2 .Net CLR知识
第二章 常用命令
2.1 基本命令
序号 |
命令 |
解释 |
.chain |
显示有哪些调试扩展。 |
|
.load DLLName !DLLName.load |
加载调试扩展。DLLName要是全路径名,包括”.dll” |
|
.loadby DLLName ModuleName |
加载调试扩展。DLLName是短文件名,不包括”.dll”。 ModuleName是调试进程中的模块名,表示通过它所在的路径查找DLLName。 |
|
.unload DLLName !DLLName.unload |
卸载调试扩展。 |
|
.setdll DLLName !DLLName.setdll |
设置缺省的调试扩展。 |
|
![ext.]address |
显示VM的分配状况 |
|
![sos.]vmmap |
显示VM的分配状况 |
|
![uext.]vadump |
输出虚拟地址映射信息 |
|
![uext.]vprot address |
显示给出的虚拟地址所在内存的映射信息。 |
|
.logfle |
检查是否有日志文件 |
|
.logopen .logappend |
打开日志文件。输出内容多时特别有用! 一个是新建,另一个是追加。 |
|
.logclose |
关闭日志文件 |
|
dt |
通过符号输出类型的结构拓扑信息。 |
|
![ntsdexts.]heap 0 0 |
察看Win32堆的运行状况。 使用!heap –stat察看heap的内存使用情况。 |
|
![ext.]dlls |
可以察看进程内的动态链接库的信息。 |
|
![sos.]threads |
可以列出所有的托管线程,并在栈顶给出异常对象的地址。 |
|
![sos.]ip2md addr |
可以将IP地址转换成IL对应的方法名。 |
|
![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 |
|
![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 |
|
![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 << |
|
![sos.]token2ee |
查找mdToken对应的名称信息。 Examples: 0:018> !Token2EE mscorlib.dll 020000c1 Module: 790c2000 (mscorlib.dll) Token: 0x020000c1 MethodTable: 790fe160 EEClass: 790fe0d0 Name: System.IntPtr |
|
c rgBuf1 L 100 rgBuf2 |
比较内存 |
|
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 |
|
ad |
删除别名,例: ad [/q] Name ad * |
|
al |
列表别名。例: al |
|
${ } |
解释(提取)别名,例: Text ${Alias} Text Text ${/d:Alias} Text Text ${/f:Alias} Text Text ${/n:Alias} Text Text ${/v:Alias} Text |
|
特别有用的MASM命令处理函数. |
$fnsucc(FnAddress, RetVal, Flag) $iment (Address) $scmp("String1", "String2") $sicmp("String1", "String2") $spat("String", "Pattern") $vvalid(Address, Length) |
|
~#s |
设置当前线程上下文。例: 0: kd> ~1s 1: kd> |
|
$<, $><, $$<, $$><, $$>a< |
(Run Script File) $<Filename $><Filename $$< Filename $$>< Filename $$>a< Filename arg1 arg2 arg3 ... argn |
|
!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 |
|
!envvar Variable |
获取环境变量的值 0:000> !envvar _nt_symbol_path _nt_symbol_path = srv*C:\mysyms*http://msdl.microsoft.com/download/symbols |
|
.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 |
|
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 |
|
![sos.]clrstack |
!CLRStack [-a] [-l] [-p] -a –l –p选项可以输出栈的参数,非常有用! |
|
![sos.]DumpStack |
!DumpStack [-EE] [top stack [bottom stack]] 比ClrStack的输出内容更为详尽,能同时显示native和.NET的堆栈信息!top stack/bottom stack是从k系列命令看到的栈顶和栈底地址。 -EE 限制只输出.NET堆栈。 如: !DumpStack 77b1dae4 77b1dba4 |
|
![exts.]tp |
!tp pool Address [Flags] !tp tqueue Address [Flags] !tp ItemType Address [Flags] !tp ThreadType [Address] !tp -? 察看OS的thread pool信息 |
|
![sos.]threadpool |
察看.NET使用的thread pool的统计信息。 |
|
x |
x [Options] Module!Symbol x [Options] *!* 检查加载的符号,特别有用! 注意,Module是模块名,不带.dll。例: 0:000> x mymodule!*spin* 0:000> x *!* |
|
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' |
|
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' " |
|
gu |
The gu command causes the target to execute until the current function is complete. User-Mode Syntax [~Thread] gu Kernel-Mode Syntax gu |
|
![sos.]findappdomain |
!findappdomain <object address> 查找对象所在的AppDomain |
|
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. |
|
# |
# (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命令的输出结果。 |
|
ld |
ld (Load Symbols) ld [ModuleName] 加载调试信息。在对付lazy load的dll时比较有用。 |
|
ln |
ln (List Nearest Symbols) ln Address The ln command displays the symbols at or near the given address. |
|
ls, lsa |
ls, lsa (List Source Lines) ls [.] [first] [, count] lsa [.] address [, first [, count]] |
|
.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 |
|
.process |
.process (Set Process Context) The .process command specifies which process is used for the process context. Syntax .process [/i] [/p [/r] ] [/P] [Process] |
|
.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] |
|
.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 |
|
.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 |
|
.writemem |
.writemem FileName Range |
|
![exts.]peb |
The !peb extension displays a formatted view of the information in the process environment block (PEB). Syntax !peb [PEB-Address] |
|
![exts.]teb |
The !teb extension displays a formatted view of the information in the thread environment block (TEB). Syntax !teb [TEB-Address] |
|
![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. |
|
![ntsdexts.]locks |
!locks [-v][-o] - Dump all Critical Sections in process |
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 // 反汇编指定地址的指令
[http://www.cnblogs.com/flier/archive/2004/07/08/22361.html]
查看进程程序集加载情况:
!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? *Exception? 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代码:
- sxe ld:mscorjit.dll
- 2. .loadby sos mscorwks
- !name2ee test.exe System.Program.Main
- ba w4 975070+0x04 "bp poi(975070+0x04);g"
- !clrstack
Ngened代码:
http://blog.joycode.com/gangp/archive/2004/04/28/20417.joy
1、!analyze -v :用于分析挂掉线程的详细情形,错误原因。
2、!locks :列出全部资源使用情况。
3、!locks -v 0x???????? :特定地址的死锁分析。
4、!thread 0x????????:特定线程详情。
5、.thread 0x????????:转到某个线程堆栈。
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
2.4. .load sos
加载微软提供的调试扩展工具,可以更方便的调试托管代码。只有加载SOS之后,才能使用下面的这些命令。
0:004> .load sos
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()
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对象
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
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
2.9. !objsize
查看对象实际占用内存大小
0:000> !objsize 01e80d5c
sizeof(01e80d5c) = 104 ( 0x68) bytes (System.String)
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[])
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
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)
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)
2.14. 转储进程
直接使用微软提供的aplus.vbs脚本转储w3wp.exe进程
Cscript adplus.vbs –hang –pn w3wp.exe –o <Dump存储目录> -quiet –do
使用Windbg命令转储进程
.dump /f <Dump存储文件>
第三章 常见问题
3.1. OutOfMemory
解决思路:
使用!dumpheap –stat命令检查托管堆对象。
使用gcroot查找数量/大小不正常对象的引用链,以明确对象没被GC回收的原因
3.2. 系统缓慢&HighCPU&单点效率异常
解决思路:
使用!clrstack命令,多次抓取系统调用栈。
比较调用栈停留位置,找到可疑处。
通过!do&!dso查看栈变量,判断代码调用栈停留位置效率低下原因。
第四章 应用示例
示例1: 缓存对象
1. 检查缓存大小
!dumpheap –stat –type System.Web.Caching.Cache //得到了 System.Web.Caching.Cache 对象的方法表
!dumpheap –mt 1230494c //得到1230494c方法表中所有对象
!objsize 03392d20 //得到当前地址对应对象的大小
2. 什么内容被缓存了?
!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: http://www.sula.cn
...
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
2. !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
3. !u 01220070
显示JIT编译了的方法的本地代码
Other:
!dumpmt -md 00983024 //得到类的成员函数详细信息
!dumpheap -stat //显示程序中所有对象的统计信息
!dumpheap -mt 00983024 //该命令显示MethodTable的详细信息
!gcroot 012919b8 //来显示一个实例的所属关系
!dumpobj(do) 012a3904 //显示一个对象的具体内容
!ObjSize 012a1ba4 //对象实际在内存中的大小
!DumpArray
//查看数组信息 (http://www.pin5i.com/showtopic-15919.html)
!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. |
第五章 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 选项按照DumpObj和DumpVC格式显示元素的细节。 -nofields 选项使数组显示不包括字段。仅当指定 -detail 选项时该选项才可用。 |
DumpAssembly <Assembly address> |
显示一个汇编集的有关信息。 如果存在多个模块,DumpAssembly命令将它们全部列出。 你可以用DumpDomain命令得到汇编集地址。 |
DumpClass <EEClass address> |
显示与一个类型相关的EEClass结构这些信息。 DumpClass命令显示静态字段值而不显示非静态字段值。 使用DumpMT、DumpObj、Name2EE、或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 选项显示在该模块中所定义的类型和被该模块引用的类型。 你可以用DumpDomain或DumpAssembly命令检索一个模块的地址。 |
DumpObj <object address> -或者- DO <object address> |
显示在指定地址上的一个对象的有关信息。DumpObj命令显示字段、EEClass结构信息、方法表和该对象的尺寸。 你可以用DumpStackObjects命令检索一个对象的地址。 注意,因为类型CLASS的字段也是对象,所以你可以对它们执行DumpObj命令。 |
DumpRuntimeTypes |
显示在垃圾收集器堆中的运行时类型对象,并列出与它们相关的类型名字和方法表。 |
DumpStack [-EE] [top stack [bottom stack]] |
显示一个栈跟踪(回溯)。 -EE 选项使DumpStack命令只显示托管函数。在x86平台上使用top和bottom参数限制所显示的栈框架。 在x86平台上,DumpStack命令创建一个冗长的栈跟踪。 在x64和基于IA-64的平台上,DumpStack命令模仿调试器的 K 命令。在x64和基于IA-64的平台上top和bottom参数被忽略。 |
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 参数限制输入内容为以下线程种类:
|
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命令检查整个托管堆和在栈以及其他对象里面句柄的句柄表。然后,在每个栈和终结器队列中搜索指向对象的指针。 这个命令不确定一个栈根是有效的还是已丢弃的。为了确定栈根是否还在使用中,需要用CLRStack和U命令反汇编局部变量或参数值所属的框架。 -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) 浏览模块,以取得适当的类型名字。你也可以传递 * 作为模块名字参数以搜索所有装入的托管模块。模块名字参数也可以是调试器给一个模块的名字,比如mscorlib或image00400000。 这个命令支持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 谢谢!