前一篇文章介绍了WinDbg入门,本篇主要介绍WinDbg常用命令和用法。 调试程序的CPU满载问题,关键是要知道程序当前正在进行什么操作。假如我们在cpu满载时创建了一个dump文件,使用下面几个命令可以查看当前程序正在进行什么处理:
.time
运行.time命令会显示时间相关的信息,如系统运行时间,进程运行时间和CPU花费在内核态和用户态的时间。
- 0:000> .time
- Debug session time: Tue Oct 23 08:38:35.000 2007 (GMT+1)
- System Uptime: 4 days 17:48:01.906
- Process Uptime: 0 days 0:24:37.000
- Kernel time: 0 days 0:04:23.000
- User time: 0 days 0:03:28.000
你可以看到系统已正常运行超过4天,进程运行了24分钟,CPU在内核态和用户态累积使用了8分钟。根据进程时间和CPU时间能大概估算出CPU使用率平均值是32.5%。
!threadpool
通过!threadpool命令我们能准确知道创建dump时cpu的使用率。也能知道在队列中的待处理请求数,Completion Port(IOCP)线程数和定时器time数。
- 0:000> !threadpool
- CPU utilization 100%
- Worker Thread: Total: 5 Running: 4 Idle: 1 MaxLimit: 200 MinLimit: 2
- Work Request in Queue: 16
- Unknown Function: 6a2d945d Context: 023ede30
- Unknown Function: 6a2d945d Context: 023ee1e8
- AsyncTimerCallbackCompletion TimerInfo@11b53760
- Unknown Function: 6a2d945d Context: 023ee3a8
- Unknown Function: 6a2d945d Context: 023e3040
- Unknown Function: 6a2d945d Context: 023ee178
- Unknown Function: 6a2d945d Context: 023edfb0
- AsyncTimerCallbackCompletion TimerInfo@11b36428
- AsyncTimerCallbackCompletion TimerInfo@11b53868
- Unknown Function: 6a2d945d Context: 023ee060
- Unknown Function: 6a2d945d Context: 023ee290
- Unknown Function: 6a2d945d Context: 023eded0
- Unknown Function: 6a2d945d Context: 023edd88
- Unknown Function: 6a2d945d Context: 023ede98
- Unknown Function: 6a2d945d Context: 023ee258
- Unknown Function: 6a2d945d Context: 023edfe8
- --------------------------------------
- Number of Timers: 9
- --------------------------------------
- Completion Port Thread:Total: 3 Free: 3 MaxFree: 4 CurrentLimit: 2 MaxLimit: 200 MinLimit: 2
我们可以看到当前CPU已100%使用率,我们进入下一命令。
!runaway
这个命令用于显示所有正在运行的线程和它们的CPU使用率。这个命令对于查找高CPU使用率问题很有帮助!
- 0:000> !runaway
- User Mode Time
- Thread Time
- 25:1a94 0 days 0:00:39.937
- 16:1bc0 0 days 0:00:38.390
- 50:1e8c 0 days 0:00:08.859
- 52:1e40 0 days 0:00:08.687
- 20:1c2c 0 days 0:00:08.234
- 51:1340 0 days 0:00:08.171
- 21:1bcc 0 days 0:00:06.953
- 26:13ec 0 days 0:00:06.671
- 44:131c 0 days 0:00:03.906
- 22:d8c 0 days 0:00:03.375
- 33:78c 0 days 0:00:02.656
- 34:1a8c 0 days 0:00:00.906
- 29:1f5c 0 days 0:00:00.828
- 6:e28 0 days 0:00:00.625
- 5:1c78 0 days 0:00:00.546
- 23:14a4 0 days 0:00:00.484
- 4:5ac 0 days 0:00:00.437
- 45:5dc 0 days 0:00:00.421
- 3:13b4 0 days 0:00:00.421
- 47:19c8 0 days 0:00:00.375
- 28:1b6c 0 days 0:00:00.250
- 46:1dac 0 days 0:00:00.156
- 7:1dd8 0 days 0:00:00.109
- 48:cdc 0 days 0:00:00.093
- 49:1eac 0 days 0:00:00.062
- 15:1a64 0 days 0:00:00.062
- 0:1804 0 days 0:00:00.046
- 36:4a4 0 days 0:00:00.031
- 11:1eb4 0 days 0:00:00.031
- 1:10b4 0 days 0:00:00.031
- 31:16ac 0 days 0:00:00.015
- 14:4ac 0 days 0:00:00.015
- 2:186c 0 days 0:00:00.015
- 59:590 0 days 0:00:00.000
- 58:294 0 days 0:00:00.000
- 57:16d0 0 days 0:00:00.000
- 56:1578 0 days 0:00:00.000
- 55:1428 0 days 0:00:00.000
- 54:16d8 0 days 0:00:00.000
- 53:fd8 0 days 0:00:00.000
- 43:1b8c 0 days 0:00:00.000
- 42:1c24 0 days 0:00:00.000
- 41:1e2c 0 days 0:00:00.000
- 40:11b0 0 days 0:00:00.000
- 39:edc 0 days 0:00:00.000
- 38:1a08 0 days 0:00:00.000
- 37:171c 0 days 0:00:00.000
- 35:1254 0 days 0:00:00.000
- 32:1f9c 0 days 0:00:00.000
- 30:1ae8 0 days 0:00:00.000
- 27:190c 0 days 0:00:00.000
- 24:1d2c 0 days 0:00:00.000
- 19:1e38 0 days 0:00:00.000
- 18:ee4 0 days 0:00:00.000
- 17:fb8 0 days 0:00:00.000
- 13:1b54 0 days 0:00:00.000
- 12:1a48 0 days 0:00:00.000
- 10:f64 0 days 0:00:00.000
- 9:1024 0 days 0:00:00.000
- 8:1b78 0 days 0:00:00.000
你可以看到运行线程的总时间和使用.time命令看到的总cpu使用时间并不相等。原因很简单,因为线程被重复使用或回收了。这也意味着一个线程的CPU使用时间可能是处理多个请求的结果。
!threads
!threads显示当前所有的托管线程信息。输出如下:
- 0:000> !threads
- ThreadCount: 48
- UnstartedThread: 0
- BackgroundThread: 29
- PendingThread: 0
- DeadThread: 19
- Hosted Runtime: no
- PreEmptive GC Alloc Lock
- ID OSID ThreadOBJ State GC Context Domain Count APT Exception
- 16 1 1bc0 001fccd0 1808220 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- 22 2 d8c 002016f0 b220 Enabled 00000000:00000000 0019daf0 0 MTA (Finalizer)
- 14 4 4ac 00242e58 880a220 Enabled 00000000:00000000 0019daf0 0 MTA (Threadpool Completion Port)
- 23 5 14a4 11b39f18 80a220 Enabled 00000000:00000000 0019daf0 0 MTA (Threadpool Completion Port)
- 24 6 1d2c 11b41ad8 1220 Enabled 00000000:00000000 0019daf0 0 Ukn
- 25 7 1a94 11b46c70 180b220 Enabled 27240c98:27241fd8 11b42540 1 MTA (Threadpool Worker)
- 26 9 13ec 12ce2888 200b220 Enabled 2a9f1434:2a9f33c0 11b42540 0 MTA
- 27 a 190c 12d85eb8 200b220 Enabled 00000000:00000000 11b42540 0 MTA
- 29 b 1f5c 13df6a50 200b220 Enabled 2ab1da6c:2ab1f1c0 11b42540 0 MTA
- 30 c 1ae8 12d44a58 b220 Enabled 00000000:00000000 11b42540 0 MTA
- 31 d 16ac 12e2e008 200b220 Enabled 2a81348c:2a8153c0 11b42540 1 MTA
- 5 e 1c78 12da2160 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- 33 8 78c 11b674c8 200b220 Enabled 2707b818:2707c1d8 11b42540 0 MTA
- 34 12 1a8c 13f163c8 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- 36 13 4a4 13eef718 200b220 Enabled 2a7db4a4:2a7dd3c0 11b42540 0 MTA
- 4 14 5ac 13ef2008 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- 42 10 1c24 13f0e950 880b220 Enabled 00000000:00000000 0019daf0 0 MTA (Threadpool Completion Port)
- 6 11 e28 13f16008 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- 3 f 13b4 13eba008 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- 43 15 1b8c 140db008 880b220 Enabled 00000000:00000000 0019daf0 0 MTA (Threadpool Completion Port)
- 44 17 131c 140ceb28 200b220 Enabled 272288c8:27229fd8 11b42540 0 MTA
- 45 1d 5dc 140cd0a0 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- 47 20 19c8 1651a008 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- XXXX 24 0 16468880 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- 46 1f 1dac 1650ab48 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- XXXX 1a 0 140d5008 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 16 0 140c5008 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- 50 3 1e8c 14064420 180b220 Enabled 27246f54:27247fd8 11b42540 1 MTA (Threadpool Worker)
- XXXX 35 0 1406e800 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- 51 36 1340 140df008 180b220 Enabled 2adec9cc:2aded1c0 11b42540 1 MTA (Threadpool Worker)
- XXXX 37 0 16566868 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- 48 38 cdc 16578840 220 Enabled 00000000:00000000 0019daf0 0 Ukn
- XXXX 39 0 16566c28 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 3b 0 1646b8b0 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 3c 0 16674008 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 3d 0 16676418 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 3e 0 16676fb8 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 3f 0 16674d48 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 40 0 1667de10 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 41 0 16680050 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 42 0 166812e8 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 43 0 16683e60 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- 52 44 1e40 165259e8 180b220 Enabled 2adf126c:2adf31c0 11b42540 1 MTA (Threadpool Worker)
- XXXX 45 0 165b7c08 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 46 0 165aa3d8 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 47 0 165242c8 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- XXXX 48 0 165e9500 1801820 Enabled 00000000:00000000 0019daf0 0 Ukn (Threadpool Worker)
- 49 3a 1eac 165676f0 220 Enabled 00000000:00000000 0019daf0 0 Ukn
线程ID为XXXX表示该线程已结束,并在等待回收。我们也能看到线程ID为22的线程正在终结(finalizer)。假如我们使用!runaway命令时看到22线程有大量的cpu活动,可能我们的程序有终结(finalizer)问题。
切换到指定线程
要切换到指定的线程,可以使用下面的格式命令:~[thread id]s,假如我们要切换到50线程,命令如下:
- 0:000> ~50s
现在我们已切换到线程50,可以使用很多其他有用的命令。
!clrstack
!clrstack显示当前线程的堆栈信息,通过指定“-p”参数,还能看到方法调用的参数和局部变量信息。 下面是线程50的堆栈信息:
- 0:050> !clrstack
- OS Thread Id: 0x1e8c (50)
- ESP EIP
- 17a9e750 7d61c828 [NDirectMethodFrameSlim: 17a9e750] System.DirectoryServices.Protocols.Wldap32.ldap_bind_s(IntPtr, System.String, System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX, System.DirectoryServices.Protocols.BindMethod)
- 17a9e768 14df70f9 System.DirectoryServices.Protocols.LdapConnection.BindHelper(System.Net.NetworkCredential, Boolean)
- 17a9e794 14df6de0 System.DirectoryServices.Protocols.LdapConnection.Bind()
- 17a9e79c 14df59e9 System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(System.DirectoryServices.Protocols.DirectoryRequest, Int32 ByRef)
- 17a9e8b8 14df56e8 System.DirectoryServices.Protocols.LdapConnection.SendRequest(System.DirectoryServices.Protocols.DirectoryRequest, System.TimeSpan)
- 17a9e8bc 14df5657 [InlinedCallFrame: 17a9e8bc]
从下向上看,我们能知道LdapConnection调用了SendRequest方法,而SendRequest又调用了SendRequestHelper方法等等。 如果我们执行“!clrstack -p”命令,我们得到信息:
- 0:050> !clrstack -p
- OS Thread Id: 0x1e8c (50)
- ESP EIP
- 17a9e750 7d61c828 [NDirectMethodFrameSlim: 17a9e750] System.DirectoryServices.Protocols.Wldap32.ldap_bind_s(IntPtr, System.String, System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX, System.DirectoryServices.Protocols.BindMethod)
- 17a9e768 14df70f9 System.DirectoryServices.Protocols.LdapConnection.BindHelper(System.Net.NetworkCredential, Boolean)
- PARAMETERS:
- this = 0x271fdfe0
- newCredential =
- needSetCredential =
-
- 17a9e794 14df6de0 System.DirectoryServices.Protocols.LdapConnection.Bind()
- PARAMETERS:
- this =
-
- 17a9e79c 14df59e9 System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(System.DirectoryServices.Protocols.DirectoryRequest, Int32 ByRef)
- PARAMETERS:
- this = 0x271fdfe0
- request = 0x27246e38
- messageID = 0x17a9e8ec
-
- 17a9e8b8 14df56e8 System.DirectoryServices.Protocols.LdapConnection.SendRequest(System.DirectoryServices.Protocols.DirectoryRequest, System.TimeSpan)
- PARAMETERS:
- this = 0x271fdfe0
- request = 0x27246e38
- requestTimeout =
-
- 17a9e8bc 14df5657 [InlinedCallFrame: 17a9e8bc]
我们可以看到DirectoryRequest参数传递给了SendRequest和SendRequestHelper,要查看DirectoryRequest的相关信息,我们只需复制它的地址(0x27246e38),并在下个命令中使用。
!dumpobject (!do)
这是另外一个重要的命令。Dumpobject会打印出指定地址的对象的相关信息。我们使用刚才的地址试下:
- 0:050> !do 0x27246e38
- Name: System.DirectoryServices.Protocols.SearchRequest
- MethodTable: 14b394c4
- EEClass: 14d97ce0
- Size: 52(0x34) bytes
- GC Generation: 0
- (C:\WINDOWS\assembly\GAC_MSIL\System.DirectoryServices.Protocols\2.0.0.0__b03f5f7f11d50a3a\System.DirectoryServices.Protocols.dll)
- Fields:
- MT Field Offset Type VT Attr Value Name
- 02c39310 4000102 4 System.String 0 instance 00000000 directoryRequestID
- 14b398bc 4000103 8 ...ControlCollection 0 instance 27246e90 directoryControlCollection
- 02c39310 4000111 c System.String 0 instance 27246d00 dn
- 12579f5c 4000112 10 ....StringCollection 0 instance 27246eb4 directoryAttributes
- 02c36ca0 4000113 14 System.Object 0 instance 27246ddc directoryFilter
- 14b39344 4000114 18 System.Int32 1 instance 1 directoryScope
- 14b393fc 4000115 1c System.Int32 1 instance 0 directoryRefAlias
- 0fd3da00 4000116 20 System.Int32 1 instance 0 directorySizeLimit
- 1202af88 4000117 28 System.TimeSpan 1 instance 27246e60 directoryTimeLimit
- 120261c8 4000118 24 System.Boolean 1 instance 0 directoryTypesOnly
通过打印的信息,可以知道它是System.DirectoryServices.Protocols.SearchRequest的一个对象,而显示的都是 System.DirectoryServices.Protocols.SearchRequest的属性值。要知道SearchRequest类的相关属性信息,可以查看MSDN。当前我们已有RequestId, Scope和DistinguishedName等等。 所以,假如我们想知道SearchRequest对象的DistinguishedName属性值,即是上面列表中的dn,我们只需再复制它的地址(27246d00),并再次使用!dumpobject命令。因为DistinguishedName是System.String类型,所以输出结果很明显:
- 0:050> !do 27246d00
- Name: System.String
- MethodTable: 02c39310
- EEClass: 0fb610ac
- Size: 112(0x70) bytes
- GC Generation: 0
- (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
- String: CN=Dummy,CN=Accounts,CN=useradm,DC=dummy,DC=net
- Fields:
- MT Field Offset Type VT Attr Value Name
- 0fd3da00 4000096 4 System.Int32 1 instance 48 m_arrayLength
- 0fd3da00 4000097 8 System.Int32 1 instance 47 m_stringLength
- 0fb80010 4000098 c System.Char 1 instance 43 m_firstChar
- 02c39310 4000099 10 System.String 0 shared static Empty
- >> Domain:Value 0019daf0:03380310 11b42540:03380310 << 0fb86d44 400009a 14 System.Char[] 0 shared static WhitespaceChars >> Domain:Value 0019daf0:03380324 11b42540:033855bc <<
通过输出的信息我们很容易知道DistinguishedName属性的值是“CN=Dummy,CN=Accounts,CN=useradm,DC=dummy,DC=net”。如果我们想查看更多内容,只需继续使用!dumpobject命令即可。
!dumpstackobjects (!dso)
使用该命令我们可以查看到当前线程的堆栈引用的所有托管对象。打印信息如下:
- 0:050> !dso
- OS Thread Id: 0x1e8c (50)
- ESP/REG Object Name
- 17a9e534 0741f860 System.RuntimeType
- 17a9e6b8 271fdfe0 System.DirectoryServices.Protocols.LdapConnection
- 17a9e6bc 27246f20 System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX
- 17a9e740 271fdfe0 System.DirectoryServices.Protocols.LdapConnection
- 17a9e744 27246f20 System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX
- 17a9e764 27246f20 System.DirectoryServices.Protocols.SEC_WINNT_AUTH_IDENTITY_EX
- 17a9e768 271fdfe0 System.DirectoryServices.Protocols.LdapConnection
- 17a9e780 271fdfe0 System.DirectoryServices.Protocols.LdapConnection
- 17a9e784 27246e38 System.DirectoryServices.Protocols.SearchRequest
- 17a9e794 271fdf14 System.DirectoryServices.Protocols.LdapDirectoryIdentifier
- 17a9e7a8 27246ef8 System.Collections.ArrayList
- 17a9e7bc 27246ef8 System.Collections.ArrayList
- 17a9e7c8 271fdfe0 System.DirectoryServices.Protocols.LdapConnection
- 17a9e8a4 27246e38 System.DirectoryServices.Protocols.SearchRequest
- 17a9e8d0 27246ed8 System.Object[] (System.Object[])
- 17a9e8e0 073ff6b8 System.String cn
- 17a9e8e4 271fdfe0 System.DirectoryServices.Protocols.LdapConnection
- 17a9e8f4 27246d00 System.String CN=Dummy,CN=Accounts,CN=useradm,DC=Dummy,DC=net
- 17a9e8f8 271fdfe0 System.DirectoryServices.Protocols.LdapConnection
- 17a9e8fc 27246e38 System.DirectoryServices.Protocols.SearchRequest
- 17a9e910 03380310 System.String
- 17a9e914 27246e24 System.Object[] (System.String[])
- 17a9e918 272399a8 System.String CN=OID-Dummy-ABC123,CN=Dummy,CN=Accounts,CN=useradm,DC=Dummy,DC=net
- 17a9e91c 27246ddc System.String (CN=OID-Dummy-ABC123)
- ...etc...
-
-
这个命令对于查找当前线程引用了那些对象很有用。假如你想查看某一对象,只需复制[Object]字段的地址,并使用 !dumpobject命令:
- 0:050> !do 271fdfe0
- Name: System.DirectoryServices.Protocols.LdapConnection
- MethodTable: 14a2040c
- EEClass: 149daf08
- Size: 56(0x38) bytes
- (C:\WINDOWS\assembly\GAC_MSIL\System.DirectoryServices.Protocols\2.0.0.0__b03f5f7f11d50a3a\System.DirectoryServices.Protocols.dll)
- Fields:
- MT Field Offset Type VT Attr Value Name
- 14a2078c 40000c3 4 ...NetworkCredential 0 instance 00000000 directoryCredential
- 14a2144c 40000c4 8 ...ificateCollection 0 instance 271fe018 certificatesCollection
- 1202af88 40000c5 10 System.TimeSpan 1 instance 271fdff0 connectionTimeOut
- 1466fe50 40000c6 c ...rectoryIdentifier 0 instance 271fdf14 directoryIdentifier
- 14a2034c 4000236 24 System.Int32 0 instance 2 connectionAuthType
- 14a223a4 4000237 18 ...dapSessionOptions 0 instance 271fe2d8 options
- 0fb896d8 4000238 28 System.IntPtr 0 instance 564180944 ldapHandle
- 120261c8 4000239 2c System.Boolean 0 instance 0 disposed
- 120261c8 400023a 2d System.Boolean 0 instance 0 bounded
- 120261c8 400023b 2e System.Boolean 0 instance 0 needRebind
- 14a22084 400023e 1c ...pResponseCallback 0 instance 271fe03c fd
- 120261c8 4000243 2f System.Boolean 0 instance 0 setFQDNDone
- 120261c8 4000244 30 System.Boolean 0 instance 1 automaticBind
- 120261c8 4000245 31 System.Boolean 0 instance 1 needDispose
- 120261c8 4000246 32 System.Boolean 0 instance 1 connected
- 14a2267c 4000247 20 ...s.QUERYCLIENTCERT 0 instance 271fe394 clientCertificateRoutine
- 0fd314bc 400023c 20 ...ections.Hashtable 0 shared static handleTable
- >> Domain:Value 0019daf0:NotInit 11b42540:073fe504 < <
- 02c36ca0 400023d 24 System.Object 0 shared static objectLock
- >> Domain:Value 0019daf0:NotInit 11b42540:073fe53c < <
- 0fd314bc 400023f 28 ...ections.Hashtable 0 shared static asyncResultTable
- >> Domain:Value 0019daf0:NotInit 11b42540:073fe610 < <
- 14a21864 4000240 2c ...lResultsProcessor 0 shared static partialResultsProcessor
- >> Domain:Value 0019daf0:NotInit 11b42540:073fe678 < <
- 12305e94 4000241 30 ....ManualResetEvent 0 shared static waitHandle
- >> Domain:Value 0019daf0:NotInit 11b42540:073fe64c < <
- 14a21954 4000242 34 ...lResultsRetriever 0 shared static retriever
- >> Domain:Value 0019daf0:NotInit 11b42540:073fe6a8 < <
-
!dumparray (!da)
你可能已经注意到有很多对象数组在堆栈中,在上面的列表中查找System.Object[]类型就能找到。如果你对对象数组使用 !dumpobject命令,你只能看到数组信息,而不能看到数组的内容信息,要看到数组内容信息,就需要使用!dumparray命令,或简称!da:
- 0:050> !do 27239b98
- Name: System.Object[]
- MethodTable: 02c3896c
- EEClass: 02c388ec
- Size: 24(0x18) bytes
- Array: Rank 1, Number of elements 2, Type CLASS
- Element Type: System.String
- Fields:
- None
-
- 0:050> !da 27239b98
- Name: System.String[]
- MethodTable: 02c3896c
- EEClass: 02c388ec
- Size: 24(0x18) bytes
- Array: Rank 1, Number of elements 2, Type CLASS
- Element Methodtable: 02c39310
- [0] 272399a8
- [1] 27239a44
-
-
通过!dumparray命令我们能知道该数组对象是字符串数组,并给出了数组项的地址。再使用!dumpobject命令我们就能看到数组项的具体内容。 !objsize 如果你查看上面打印的信息,可以看到对象的大小是24字节。从某方面来说,这是对的,24字节是System.Object[]数组对象本身大小,但并不包括数组内容的大小!要获得整个对象的大小,就要使用到 !objsize命令:
- 0:050> !objsize 27239b98
- sizeof(27239b98) = 348 ( 0x15c) bytes (System.Object[])
-
!objsize会遍历对象引用的所有子对象,并计算出总的大小。如上面数组对象和它的内容的总大小是348字节。 如果对象有引用很多子对象,那么!objsize会花费较多的时间计算出总大小。
!dumpheap
这是另一个较频繁使用的命令。!dumpheap会打印出所有在托管堆中的对象信息。直接执行该命令会打印出大量的信息,所以一般使用时都至少带上一个参数。加上-stat参数后会输出总结后的信息,如下面是截取的是!dumpheap -stat命令输出的一部分:
- 0:050> !dumpheap -stat
- ------------------------------
- Heap 0
- total 2754508 objects
- ------------------------------
- Heap 1
- total 2761329 objects
- ------------------------------
- total 5515837 objects
- Statistics:
- MT Count TotalSize Class Name
- 16e0a6d8 1 12 System.Collections.Generic.ObjectEqualityComparer`1[[System.Data.ProviderBase.DbConnectionInternal, System.Data]]
- 16d9cd9c 1 12 System.Xml.Serialization.Configuration.DateTimeSerializationSection+DateTimeSerializationMode
- 16d9bf30 1 12 System.Diagnostics.OrdinalCaseInsensitiveComparer
- 16d9112c 1 12 System.Xml.Serialization.NameTable
- 16d7f664 1 12 System.Xml.Serialization.TempAssemblyCache
- 163ea85c 1 12 System.Data.Res
- 1501e4c4 1 12 System.Collections.Generic.ObjectEqualityComparer`1[[System.Web.UI.Control, System.Web]]
- 14efb138 1 12 System.Net.TimeoutValidator
- 14ef9964 1 12 System.Net.Cache.HttpRequestCacheLevel
- 14ef77a8 1 12 Microsoft.Win32.WinInetCache
- 14ef68e4 1 12 System.Net.WebRequest+WebProxyWrapper
- 14ef658c 1 12 System.Net.Configuration.ProxyElement+BypassOnLocalValues
- 14ef63d8 1 12 System.Net.Configuration.ProxyElement+AutoDetectValues
- 14ef5b68 1 12 System.Net.CaseInsensitiveAscii
- 14ef5610 1 12 System.Net.HeaderInfoTable
- 14ef4718 1 12 System.Net.HttpRequestCreator
- 14db6710 1 12 System.Web.Configuration.MachineKeyValidationConverter
- 14db3140 1 12 System.Collections.Generic.ObjectEqualityComparer`1[[System.Runtime.Serialization.MemberHolder, mscorlib]]
- 14b3f4d8 1 12 System.Web.UI.SupportsEventValidationAttribute
- ...etc...
- 14a276a8 19578 704808 System.DirectoryServices.Interop.AdsValueHelper
- 14a2ea24 9196 735680 System.Web.UI.WebControls.Label
- 14a2e51c 16862 741928 System.Web.UI.WebControls.Style
- 125778ec 48015 768240 System.Collections.Specialized.NameObjectCollectionBase+NameObjectEntry
- 120261c8 65842 790104 System.Boolean
- 14a2ee7c 9198 809424 System.Web.UI.WebControls.Table
- 14b311c4 9647 810348 System.Web.UI.WebControls.Image
- 13a2b7dc 34913 837912 System.Web.HttpServerVarsCollectionEntry
- 14b303a4 10605 848400 System.Web.UI.WebControls.HyperLink
- 14d8e3d4 77748 932976 Microsoft.Web.UI.WebControls.BaseChildNodeCollection+ActionType
- 14db90ac 81372 976464 System.Web.UI.WebControls.FontInfo
- 14b30694 28648 1031328 System.Web.UI.WebControls.TableRow+CellControlCollection
- 14d8fdbc 38912 1089536 Microsoft.Web.UI.WebControls.TreeNodeCollection
- 14b3d0bc 86592 1385472 System.Web.UI.Pair
- 1466c5c4 39305 1414980 System.Web.UI.ControlCollection
- 14d8e48c 77748 1865952 Microsoft.Web.UI.WebControls.BaseChildNodeCollection+Action
- 1545061c 38874 2176944 Microsoft.Web.UI.WebControls.TreeNode
- 14b30eec 52668 2317392 System.Web.UI.WebControls.TableItemStyle
- 14a2f804 28515 2395260 System.Web.UI.WebControls.TableRow
- 14a2be6c 40894 2453640 System.Web.UI.LiteralControl
- 0fd3da00 228792 2745504 System.Int32
- 14b3e3ac 244793 2937516 System.Web.UI.IndexedString
- 14a2de94 198580 3177280 System.Web.UI.StateBag
- 1466c454 80512 3542528 System.Web.UI.Control+OccasionalFields
- 12302c2c 205849 4116980 System.Collections.Specialized.HybridDictionary
- 14b30024 52934 4446456 System.Web.UI.WebControls.TableCell
- 12302f2c 178294 4992232 System.Collections.Specialized.ListDictionary
- 14a2e284 412762 6604192 System.Web.UI.StateItem
- 14d8ce64 117078 7024680 Microsoft.Web.UI.WebControls.CssCollection
- 0fd314bc 132065 7395640 System.Collections.Hashtable
- 1230319c 422580 8451600 System.Collections.Specialized.ListDictionary+DictionaryNode
- 1202a58c 380438 9130512 System.Collections.ArrayList
- 0fd32050 133000 22582944 System.Collections.Hashtable+bucket[]
- 02c3896c 649842 23275900 System.Object[]
- 0fd3c12c 3471 36385536 System.Byte[]
- 001fee20 338 65409920 Free
- 02c39310 683083 161821000 System.String
- Total 5515837 objects
- Fragmented blocks larger than 0.5 MB:
- Addr Size Followed by
- 2adf31cc 2.0MB 2aff85d8 System.String
- 2b006a2c 20.3MB 2c4530d8 System.Net.SocketAddress
-
-
输出信息按对象类型的总大小升序排序,你一般可以在列表的最下面找到string对象的大小,因为字符串在程序中一般是最常用的。 其他比较有用的参数是-type和-mt(MethodTable的意思)。使用它们你可以查看指定对象类型的详细信息,例如假如我们想查看System.Net.HttpRequestCreator类型的具体信息,可以复制上面列表中它的MT字段地址(14ef4718),然后使用 !dumpheap -mt命令:
- 0:050> !dumpheap -mt 14ef4718
- ------------------------------
- Heap 0
- Address MT Size
- 0342ccf8 14ef4718 12
- total 1 objects
- ------------------------------
- Heap 1
- Address MT Size
- total 0 objects
- ------------------------------
- total 1 objects
- Statistics:
- MT Count TotalSize Class Name
- 14ef4718 1 12 System.Net.HttpRequestCreator
-
上面列出了所有System.Net.HttpRequestCreator类型的对象的地址,假如我们想查看指定对象的信息,再使用!dumpobject命令就可以了。 !dumpheap -type可以根据字符串来匹配对应的对象类型。如我们输入命令“!dumpheap -type System.Web”,是指显示所有类名包含有“System.Web”字符串的对象信息。 其他参数-min和-max是接受表示对象大小的最大值和最小值(单位字节),命令会只列出大于指定值或小于指定值的对象信息。这两个参数对于查找滥用字符串问题很有帮助。
实战训练
查明缓存使用的大小
为了知道在System.Web.Caching.Cache类型中有多少数据,我执行了“ !dumpheap -stat -type System.Web.Caching.Cache”命令。注意,我用了-stat参数,否则我会得到一个包含有System.Web.Caching.CacheKeys和System.Web.Caching.CacheEntrys对象的很长的列表。下面的执行结果:
- 0:050> !dumpheap -type System.Web.Caching.Cache -stat
- ------------------------------
- Heap 0
- total 665 objects
- ------------------------------
- Heap 1
- total 1084 objects
- ------------------------------
- total 1749 objects
- Statistics:
- MT Count TotalSize Class Name
- 123056f8 1 12 System.Web.Caching.CacheKeyComparer
- 1230494c 1 12 System.Web.Caching.Cache
- 1230500c 1 24 System.Web.Caching.CacheMultiple
- 1230514c 1 32 System.Web.Caching.CacheMemoryStats
- 123053b4 1 36 System.Web.Caching.CacheMemoryTotalMemoryPressure
- 123059bc 2 40 System.Web.Caching.CacheUsage
- 12304bdc 1 48 System.Web.Caching.CacheCommon
- 123054f4 1 52 System.Web.Caching.CacheMemoryPrivateBytesPressure
- 12305874 2 64 System.Web.Caching.CacheExpires
- 12304e64 2 200 System.Web.Caching.CacheSingle
- 1255b594 85 1360 System.Web.Caching.CacheDependency+DepFileInfo
- 123046c4 40 1440 System.Web.Caching.CacheDependency
- 123042ec 47 1504 System.Web.Caching.CacheItemRemovedCallback
- 123063fc 832 16640 System.Web.Caching.CacheKey
- 12306820 732 52704 System.Web.Caching.CacheEntry
- Total 1749 objects
-
-
很明显System.Web.Caching.Cache的方法列表地址(MethodTable)是1230494c,再使用!dumpheap命令我就能看到它的所有对象信息,如下:
- 0:050> !dumpheap -mt 1230494c
- ------------------------------
- Heap 0
- Address MT Size
- 03392d20 1230494c 12
- total 1 objects
- ------------------------------
- Heap 1
- Address MT Size
- total 0 objects
- ------------------------------
- total 1 objects
- Statistics:
- MT Count TotalSize Class Name
- 1230494c 1 12 System.Web.Caching.Cache
- Total 1 objects
-
可以看到System.Web.Caching.Cache类型只有一个对象,地址是03392d20,再通过!objsize命令就能计算出它的大小。因为缓存对象很复杂,并包含有大量的子对象,要计算出总大小需要花些时间:
- 0:050> !objsize 03392d20
- sizeof(03392d20) = 266640828 ( 0xfe49dbc) bytes (System.Web.Caching.Cache)
-
所以缓存的总大小是 266 MB。
缓存了什么东西?
为了弄清缓存中保存了什么内容,我查看了System.Web.Caching.CacheEntry的对象信息。通过之前的信息可以知道System.Web.Caching.CacheEntry类型的方法列表(MethodTable)是12306820。执行!dumpheap命令取CacheEntry的所有对象信息:
- 0:050> !dumpheap -mt 12306820
- ------------------------------
- Heap 0
- Address MT Size
- 033950bc 12306820 72
- 033a20d8 12306820 72
- 033ac79c 12306820 72
- 033da21c 12306820 72
- 033f04c4 12306820 72
- 03428ec8 12306820 72
- 0344dab4 12306820 72
- 03815d00 12306820 72
- 038265d8 12306820 72
- ....etc...
- 03af7010 12306820 72
- 03b291bc 12306820 72
- 03b2c674 12306820 72
- 03b6dca0 12306820 72
- 03b797dc 12306820 72
- 03b85318 12306820 72
- 03ba9150 12306820 72
- 03c258cc 12306820 72
- 03de43c8 12306820 72
- 03e160f8 12306820 72
- total 382 objects
- ------------------------------
- total 732 objects
-
要显示上面的信息,也可以使用“!dumpheap -type System.Web.Caching.CacheEntry”命令。 有了所有CacheEntry对象的地址信息,我随机拿了一个地址查看它的内容:
- 0:050> !do 03b2c674
- Name: System.Web.Caching.CacheEntry
- MethodTable: 12306820
- EEClass: 122f6470
- Size: 72(0x48) bytes
- (C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
- Fields:
- MT Field Offset Type VT Attr Value Name
- 02c39310 4001327 4 System.String 0 instance 03b2c600 _key
- 0fb8f1f8 4001328 c System.Byte 0 instance 2 _bits
- 0fd3da00 4001329 8 System.Int32 0 instance -1314181915 _hashCode
- 02c36ca0 4001330 10 System.Object 0 instance 03b2c644 _value
- 120219d0 4001331 1c System.DateTime 1 instance 03b2c690 _utcCreated
- 120219d0 4001332 24 System.DateTime 1 instance 03b2c698 _utcExpires
- 1202af88 4001333 2c System.TimeSpan 1 instance 03b2c6a0 _slidingExpiration
- 0fb8f1f8 4001334 d System.Byte 0 instance 7 _expiresBucket
- 123062d8 4001335 34 ...g.ExpiresEntryRef 1 instance 03b2c6a8 _expiresEntryRef
- 0fb8f1f8 4001336 e System.Byte 0 instance 4294967295 _usageBucket
- 12306738 4001337 38 ...ing.UsageEntryRef 1 instance 03b2c6ac _usageEntryRef
- 120219d0 4001338 3c System.DateTime 1 instance 03b2c6b0 _utcLastUpdate
- 123046c4 4001339 14 ...g.CacheDependency 0 instance 00000000 _dependency
- 02c36ca0 400133a 18 System.Object 0 instance 033d8344 _onRemovedTargets
- 120219d0 400132d 1bc System.DateTime 1 shared static NoAbsoluteExpiration
- >> Domain:Value 0019daf0:NotInit 11b42540:03395104 < <
- 1202af88 400132e 1c0 System.TimeSpan 1 shared static NoSlidingExpiration
- >> Domain:Value 0019daf0:NotInit 11b42540:03395114 < <
- 1202af88 400132f 1c4 System.TimeSpan 1 shared static OneYear
- >> Domain:Value 0019daf0:NotInit 11b42540:03395124 < <
-
输出的是CacheEntry的属性信息,里面最重要的是_value属性。我复制它的地址(03b2c644),再用!dumpobject命令查看:
- 0:000> !do 03e160c8
- Name: System.Web.SessionState.InProcSessionState
- MethodTable: 14dbad5c
- EEClass: 14e43af8
- Size: 48(0x30) bytes
- (C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
- Fields:
- MT Field Offset Type VT Attr Value Name
- 1466c9d8 4001d89 4 ...ateItemCollection 0 instance 1a7f5438 _sessionItems
- 1292672c 4001d8a 8 ...ObjectsCollection 0 instance 00000000 _staticObjects
- 0fd3da00 4001d8b c System.Int32 0 instance 20 _timeout
- 120261c8 4001d8c 18 System.Boolean 0 instance 0 _locked
- 120219d0 4001d8d 1c System.DateTime 1 instance 03e160e4 _utcLockDate
- 0fd3da00 4001d8e 10 System.Int32 0 instance 1 _lockCookie
- 1202bf60 4001d8f 24 ...ReadWriteSpinLock 1 instance 03e160ec _spinLock
- 0fd3da00 4001d90 14 System.Int32 0 instance 0 _flags
-
可以看到缓存的是一个InProcSessionState对象。