天天看點

檢視w3wp程序占用的記憶體及.NET記憶體洩露,死鎖分析

一 基礎知識

 在分析之前,先上一張圖:

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析

從上面可以看到,這個w3wp程序占用了376M記憶體,啟動了54個線程。

在使用windbg檢視之前,看到的程序含有 *32 字樣,意思是在64位機器上已32位方式運作w3wp程序。這個可以通過檢視IIS Application Pool 的進階選項進行設定:

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析
好了,接下打開Windbg看看這個w3wp程序占用了376M記憶體,啟動的54個線程。
檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析

1. 加載 WinDbg SOS 擴充指令

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

2. !dumpheap -stat

!DumpHeap 将周遊 GC 堆對對象進行分析。

  MT            Count    TotalSize       Class Name

78eb9834        1           12       System.ServiceModel.ServiceHostingEnvironment+HostingManager+ExtensionHelper

0118c800      101        14824      Free

...

63ce0004    19841      1111096 System.Reflection.RuntimeMethodInfo

63ce2ee4    11080      2061036 System.Int32[]

63ce0d48    34628      2242596 System.String

63ce37b8    20012      3264884 System.Byte[]

63cb4518   157645      4940676 System.Object[]

Total 524310 objects

可以看到,w3wp上總共有524310個對象, 共占用了這些記憶體。

我們可以将上述上述列出的這些對象歸為2類:

1). 有根對象(在應用程式中對這些對象存在引用)

2). 自從上次垃圾回收之後新建立或無跟對象

要注意的是Free這項:

這項一般都是GC not yet Compacted的空間或一些堆上配置設定的禁止GC compacted釘扣對象.

第一欄 : 類型的方法清單 MT(method type for the type)

第二欄:堆上的對象數量

第三欄:所有同類對象的總大小

3. !dumpheap -mt 63ce0d48   

檢視 63ce0d48  單元的有哪些對象。

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析

4. !do 103b3360

看看103b3360位址的string包含哪些内容

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析

可見,103b3360位址的字元串value="System.Web.UI.PageRequestManager:AsyncPostBackError", 占120bytes. 這個字元串對象包含3個字段,它們的偏移量分别是4,8,12。

5. dd 103b3360

看看103b3360的值

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析

從左往右第一列是位址,而第二列開始則是位址上的資料。

6. !dumpheap -type String -min 100

看看堆上所有大于100位元組的字元串。 注意:假如 -min 85000(大于85000位元組的字元串或對象将存儲在大對象堆上). 

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析

二. NET記憶體洩露分析案例

1 基礎認識

  .net世界裡,GC是負責垃圾回收的,但GC僅僅是回收哪些不可及的對象(無根對象),對于有應用的有根對象,GC對此無能為力。

  .net一些記憶體洩漏的根本原因:

  • 使用靜态引用
  • 未退訂的事件-作者認為這是最常見的記憶體洩漏原因
  • 未退訂的靜态事件
  • 未調用Dispose方法
  • 使用不徹底的Dispose方法
  • 在Windows Forms中對BindingSource的誤用
  • 未在WorkItem/CAB上調用Remove

一些避免記憶體洩漏的建議:

  • 對象的建立者或擁有者負責銷毀對象,而不是使用者
  • 當不再需要一個事件訂閱者時退訂此事件,為確定安全可以在Dispose方法中退訂
  • 當對象不再觸發事件時,應該将對象設為null來移除所有的事件訂閱者
  • 當模型和視圖引用同一個對象時,推薦給視圖傳遞一個此對象的克隆,以防止無法追蹤誰在使用哪個對象
  • 對系統資源的通路應該包裝在using塊中,這将在代碼執行後強制執行Dispose

對這些做基本了解後,我們将步入正題。

2. 案例分析

先上測試代碼: 

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析
檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析
1 public class LeakTest
 2     {
 3         private static string leakString;
 4 
 5         public LeakTest()
 6         {
 7             for (int i = 0; i < 1000; i++)
 8             {
 9                 leakString += "LEAK";
10             }
11         }
12 
13         public string GetRamdonString()
14         {
15             System.Random random = new System.Random();
16 
17             string str = string.Empty;
18             for (int i = 0; i < 25; i++)
19             {
20                 str += str + random.Next(100);
21             }
22             return str;
23         }
24 
25         public void NoDispose()
26         {
27             string str = GetRamdonString();
28             
29             ZipFile zip = new ZipFile();
30             zip.AddEntry("a.txt", str);
31             zip.AddEntry("b.txt", str);
32             zip.Save("test.rar");
33             //zip.Dispose();
34         }
35     }
36 
37     class Program
38     {
39         static void Main(string[] args)
40         {
41             LeakTest leakTest = new LeakTest();
42             leakTest.NoDispose();
43             Console.ReadLine();
44         }
45     }      

View Code

需要說明的是:

這裡程式裡面定義了一個Static 字元串,及使用了Ionic.Zip 這個Zip壓縮包,僅僅是為了模拟記憶體堆積現象,沒有調用zip.Dispose()方法,事實上Ionic.Zip并不會造成記憶體洩露。

正式開始了:

啊哈,好極了。 運作程式,好家夥,果然很耗費記憶體! 這麼個小程式,吃了287M,并啟動了12個線程.

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析

0:005> .load C:\Windows\Microsoft.NET\Framework64\v2.0.50727\sos.dll 

0:005> .load C:\Symbols\sosex_64\sosex.dll

0:005> !dumpheap -stat

1 0:012> !dumpheap -stat
 2 PDB symbol for mscorwks.dll not loaded
 3 total 12840 objects
 4 Statistics:
 5               MT    Count    TotalSize Class Name
 6 000007ff001d2248        1           24 System.Collections.Generic.Dictionary`2+ValueCollection[[System.String, mscorlib],[Ionic.Zip.ZipEntry, Ionic.Zip.Reduced]]
 7 000007ff000534f0        1           24 ZipTest.LeakTest
 8 000007fee951e8e8        1           24 System.IO.TextReader+NullTextReader
 9 000007fee94f8198        1           24 System.Security.Cryptography.RNGCryptoServiceProvider

11 ...
43 000007ff001d9130     1041        66624 Ionic.Zlib.DeflateManager+CompressFunc
44 000007fee94d2d40     1023        73656 System.Threading.ExecutionContext
45 000007fee951e038     3075      1387592 System.UInt32[]
46 000007fee951ca10     3179      2450704 System.Int16[]
47 0000000000207800      261     67034512      Free
48 000007fee94d7d90      514    134251544 System.String
49 000007fee94dfdd0      102    138593344 System.Byte[]
50 Total 12840 objects      

果然,我們看到了裡面有2類大對象,分别占用了134M和138M . 好家夥!

0:005> !dumpheap -mt 

1 0:012> !dumpheap -mt 000007fee94dfdd0      
  2          Address               MT     Size
  3...   
 24 00000000026f11f0 000007fee94dfdd0    65560     
 25 0000000002701288 000007fee94dfdd0    65560     
 26 00000000027112a0 000007fee94dfdd0    65592     
 27 0000000002722b50 000007fee94dfdd0    65560     
 28 0000000002752b98 000007fee94dfdd0    65560     
 29 ...    
 47 000000000290ab98 000007fee94dfdd0    65560     
 48 000000000293abe0 000007fee94dfdd0    65560     
 49 ...     
 64 0000000002ac1378 000007fee94dfdd0    65560     
 65 0000000002ad1410 000007fee94dfdd0    65560     
 66...  
103 00000000165a71e0 000007fee94dfdd0 67108888     
104 0000000027c11000 000007fee94dfdd0 67108888     
105 total 102 objects
106 Statistics:
107               MT    Count    TotalSize Class Name
108 000007fee94dfdd0      102    138593344 System.Byte[]
109 Total 102 objects      

果然,有那麼多65592和65560啊 啊

随便找一個看一下:

0:005> !do 0000000002ba4790

1 0:012> !do 0000000002ba4790 
2 Name: System.Byte[]
3 MethodTable: 000007fee94dfdd0
4 EEClass: 000007fee90e26b0
5 Size: 65590(0x10036) bytes
6 Array: Rank 1, Number of elements 65566, Type Byte
7 Element Type: System.Byte
8 Fields:
9 None      

哦。這是個一維的數組,有65566位元組,推測應該好像是short(int16)長度。

繼續,

!gcroot 0000000002b42dd0

0:012> !gcroot 0000000002b42dd0 
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 1d3c
RSP:18ef58:Root:00000000025c5b88(Ionic.Zip.ZipFile)->
00000000025d2578(Ionic.Zlib.ParallelDeflateOutputStream)->
00000000025dc528(System.Collections.Generic.List`1[[Ionic.Zlib.WorkItem, Ionic.Zip.Reduced]])->
000000000294ac38(System.Object[])->
0000000002b32d78(Ionic.Zlib.WorkItem)->
0000000002b42dd0(System.Byte[])
...
Scan Thread 10 OSTHread 3718      

這裡有點看頭了! 看其跟對象 Ionic.Zip.ZipFile 這個對象占着沒銷毀的記憶體呢!

 RSP:18ef58:Root:00000000025c5b88(Ionic.Zip.ZipFile)->

00000000025d2578(Ionic.Zlib.ParallelDeflateOutputStream)->

00000000025dc528(System.Collections.Generic.List`1[[Ionic.Zlib.WorkItem, Ionic.Zip.Reduced]])->

000000000294ac38(System.Object[])->

0000000002b32d78(Ionic.Zlib.WorkItem)->

0000000002b42dd0(System.Byte[])

換一個看看:

0:012> !gcroot 00000000029bc730 
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 1d3c
RSP:18ef58:Root:00000000025c5b88(Ionic.Zip.ZipFile)->
00000000025d2578(Ionic.Zlib.ParallelDeflateOutputStream)->
00000000025dc528(System.Collections.Generic.List`1[[Ionic.Zlib.WorkItem, Ionic.Zip.Reduced]])->
000000000294ac38(System.Object[])->
00000000029ac6d8(Ionic.Zlib.WorkItem)->
00000000029bc730(System.Byte[])
...
Scan Thread 10 OSTHread 3718      

檢視下其代齡:

0:012> !gcgen 00000000029bc730

GEN 1

看到了,這個byte[]在1代。

到此為止,還記得有個靜态字元串吧

private static string leakString;

我們回頭再去看看,

 !dumpheap -type String -min 1000

0:012> !dumpheap -type String -min 1000
         Address               MT     Size
00000000025c26e0 000007fee94d7d90     8032     
00000000025cca30 000007fee94d7d90     1176     
00000000025cd308 000007fee94d7d90     1600     
000000001ae81000 000007fee94d7d90 134215704     
total 4 objects
Statistics:
              MT    Count    TotalSize Class Name
000007fee94d7d90        4    134226512 System.String
Total 4 objects      

Next,

0:012> !do 00000000025c26e0

0:012> !do 00000000025c26e0 
Name: System.String
MethodTable: 000007fee94d7d90
EEClass: 000007fee90de560
Size: 8026(0x1f5a) bytes
 (C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: LEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKL....
EAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAK
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fee94df000  4000096        8         System.Int32  1 instance             4001 m_arrayLength
000007fee94df000  4000097        c         System.Int32  1 instance             4000 m_stringLength
000007fee94d97d8  4000098       10          System.Char  1 instance               4c m_firstChar
000007fee94d7d90  4000099       20        System.String  0   shared           static Empty
                                 >> Domain:Value  000000000062b1d0:00000000025c1308 <<
000007fee94d9688  400009a       28        System.Char[]  0   shared           static WhitespaceChars
                                 >> Domain:Value  000000000062b1d0:00000000025c1a90 <<      

 再看下這個對象:

!dumpobj 00000000025c26e0

0:012> !dumpobj 00000000025c26e0 
Name: System.String
MethodTable: 000007fee94d7d90
EEClass: 000007fee90de560
Size: 8026(0x1f5a) bytes
 (C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)      
(C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: LEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKL....
EAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAKLEAK
Fields:      
MT Field Offset Type VT Attr Value Name 000007fee94df000 4000096 8 System.Int32 1 instance 4001 m_arrayLength 000007fee94df000 4000097 c System.Int32 1 instance 4000 m_stringLength 000007fee94d97d8 4000098 10 System.Char 1 instance 4c m_firstChar 000007fee94d7d90 4000099 20 System.String 0 shared static Empty >> Domain:Value 000000000062b1d0:00000000025c1308 << 000007fee94d9688 400009a 28 System.Char[] 0 shared static WhitespaceChars >> Domain:Value 000000000062b1d0:00000000025c1a90 <<      

顯示結果一樣,String:LEAKLEAKLEAKLEAKLEAK......,字元串長度4000,和我們的測試代碼吻合:

1  public LeakTest()
2         {
3             for (int i = 0; i < 1000; i++)
4             {
5                 leakString += "LEAK";
6             }
7         }      

到此,記憶體檢視分析示範的差不多了!

這裡我們示範的是個小得不能再小的程式,且存在前提預期。 假如在實際項目環境中,因為引用的DLL多,生成的對象繁雜,實際診斷問題根源就複雜得多,這就需要比較紮實的基本功。

三. 死鎖排查

 1. 基礎

還是用上面的Console App例子,運作這個程式,啟動了13個線程。我們先看一下這13個線程:

!runaway

0:012> !runaway
 User Mode Time
  Thread       Time
   0:5588      0 days 0:00:05.085
   7:4954      0 days 0:00:01.903
   3:4ddc      0 days 0:00:01.825
   8:5af4      0 days 0:00:01.809
   9:4740      0 days 0:00:01.747
  10:6c38      0 days 0:00:01.731
   4:6a94      0 days 0:00:01.700
   5:43ec      0 days 0:00:01.622
   6:8fdc      0 days 0:00:01.606
  12:1e64      0 days 0:00:00.000
  11:6a4       0 days 0:00:00.000
   2:64b4      0 days 0:00:00.000
   1:69e4      0 days 0:00:00.000      

恩。13個線程,沒錯。 這裡還可以看到每個線程的執行時間。 其中 0 線程占用時間最多。我們去看下堆棧調用:

~0s

!ClrStack -a

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析
檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析
0:012> ~0s
ntdll!ZwRequestWaitReplyPort+0xa:
00000000`77b714da c3              ret
0:000> !ClrStack -a
OS Thread Id: 0x5588 (0)
*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v2.0.50727_64\mscorlib\c3beeeb6432f004b419859ea007087f1\mscorlib.ni.dll
Child-SP         RetAddr          Call Site
00000000001de670 000007fee9b02c79 DomainNeutralILStubClass.IL_STUB(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
    PARAMETERS:
        <no data>
        <no data>
        <no data>
        <no data>
        <no data>

00000000001de790 000007fee9b02d92 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Int32, Int32 ByRef)
    PARAMETERS:
        hFile = <no data>
        bytes = <no data>
        offset = <no data>
        count = <no data>
        mustBeZero = <no data>
        errorCode = 0x00000000001de820
    LOCALS:
        <no data>
        0x00000000001de7c0 = 0x0000000000000000
        <no data>
        <no data>

00000000001de7f0 000007fee93f08da System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
    PARAMETERS:
        this = <no data>
        buffer = <no data>
        offset = <no data>
        count = <no data>
    LOCALS:
        0x00000000001de820 = 0x0000000000000000
        <no data>

00000000001de850 000007fee9412a8a System.IO.StreamReader.ReadBuffer()
    PARAMETERS:
        this = <no data>
    LOCALS:
        <no data>

00000000001de8a0 000007fee9b0622f System.IO.StreamReader.ReadLine()
    PARAMETERS:
        this = <no data>
    LOCALS:
        <no data>
        <no data>
        <no data>
        <no data>

00000000001de8f0 000007ff00190188 System.IO.TextReader+SyncTextReader.ReadLine()
    PARAMETERS:
        this = 0x00000000030387b0

00000000001de950 000007feea23c6a2 ZipTest.Program.Main(System.String[])
    PARAMETERS:
        args = 0x00000000027e2680
    LOCALS:
        0x00000000001de970 = 0x00000000027e26a0      

瞧準了,這是個主線程,他在等待Console.ReadLine(). 是以占用了這麼長時間。

再在看一下這13個線程裡,哪些是托管堆線程:

!threads

0:012> !threads
ThreadCount: 10
UnstartedThread: 0
BackgroundThread: 9
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                              PreEmptive                                                Lock
       ID OSID        ThreadOBJ     State   GC     GC Alloc Context                  Domain           Count APT Exception
   0    1 5588 00000000009d4510      a020 Enabled  00000000030387d0:000000000303a510 00000000009cb1d0     1 MTA
   2    2 64b4 00000000009dc4d0      b220 Enabled  0000000000000000:0000000000000000 00000000009cb1d0     0 MTA (Finalizer)
   3    3 4ddc 0000000000a1a010   180b220 Enabled  0000000002fe1e28:0000000002fe2450 00000000009cb1d0     0 MTA (Threadpool Worker)
   4    4 6a94 0000000000a1d590   180b220 Enabled  0000000002fe73c8:0000000002fe8450 00000000009cb1d0     0 MTA (Threadpool Worker)
   5    5 43ec 0000000000a7bbd0   180b220 Enabled  0000000002fec968:0000000002fee450 00000000009cb1d0     0 MTA (Threadpool Worker)
   6    6 8fdc 0000000000a892b0   180b220 Enabled  0000000002ff0968:0000000002ff2450 00000000009cb1d0     0 MTA (Threadpool Worker)
   7    7 4954 0000000000aa3270   180b220 Enabled  0000000002fee968:0000000002ff0450 00000000009cb1d0     0 MTA (Threadpool Worker)
   8    8 5af4 0000000000a97eb0   180b220 Enabled  0000000002fe8968:0000000002fea450 00000000009cb1d0     0 MTA (Threadpool Worker)
   9    9 4740 0000000000a99400   180b220 Enabled  0000000002fe0358:0000000002fe0450 00000000009cb1d0     0 MTA (Threadpool Worker)
  10    a 6c38 0000000000a9f3a0   180b220 Enabled  0000000002fe3e28:0000000002fe4450 00000000009cb1d0     0 MTA (Threadpool Worker)      

在托管堆上啟動的線程有10個。這10個線程分别是什麼,繼續看:

0号MTA: 程式主線程

MTA (Finalizer):這個是Finalizer線程,該線程負責垃圾對象回收。

MTA (Threadpool Worker):這些是ThreadPool建立的線程,這裡是Ionic.Zlib.WorkItem産生的工作線程。

另外,CLR根據需要還會開啟其他一些線程,如:

并發的GC線程 ,伺服器GC線程 ,調試器幫助線程 ,AppDomain解除安裝線程 等.

看一下同步塊情況,有麼有死鎖?

!syncblk

!dlk

0:003> !dlk
Examining SyncBlocks...
Scanning for ReaderWriterLock instances...
Scanning for holders of ReaderWriterLock locks...
Scanning for ReaderWriterLockSlim instances...
Scanning for holders of ReaderWriterLockSlim locks...
Examining CriticalSections...
No deadlocks detected.      

顯示該程式沒有鎖相關資源,實際确實沒有。

2 死鎖

Lock:lock 關鍵字将語句塊标記為臨界區,方法是擷取給定對象的互斥鎖,執行語句,然後釋放該鎖。 下面的示例包含一個 lock 語句。

lock 關鍵字可確定當一個線程位于代碼的臨界區時,另一個線程不會進入該臨界區。 如果其他線程嘗試進入鎖定的代碼,則它将一直等待(即被阻止),直到該對象被釋放。

通常,應避免鎖定 public 類型,否則執行個體将超出代碼的控制範圍。 常見的結構 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 違反此準則:

  • 如果執行個體可以被公共通路,将出現 lock (this) 問題。
  • 如果 MyType 可以被公共通路,将出現 lock (typeof (MyType)) 問題。
  • 由于程序中使用同一字元串的任何其他代碼都将共享同一個鎖,是以出現 lock("myLock") 問題。

最佳做法是定義 private 對象來鎖定, 或 private static 對象變量來保護所有執行個體所共有的資料。

3 案例分析

這個案例很簡單,上菜:

檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析
檢視w3wp程式占用的記憶體及.NET記憶體洩露,死鎖分析
1   public class Consumer1
 2     {
 3         private string connString;
 4         public Consumer1(string str)
 5         {
 6             this.connString = str;
 7         } 
 8     }
 9 
10     public class Consumer2
11     {
12         private string connString;
13         public Consumer2(string str)
14         {
15             this.connString = str;
16         }
17     }
18 
19     class Program
20     {
21         private static Consumer1 consumer1;
22         private static Consumer2 consumer2;
23 
24         static void Main(string[] args)
25         {
26             consumer1 = new Consumer1("Conn1");
27             consumer2 = new Consumer2("Conn2");
28 
29             Thread thread = new Thread(Proc);
30             thread.Start();
31              
32             lock (consumer2)
33             {
34                 Console.WriteLine("Proc->Lock consumer2");
35                 Thread.Sleep(1000);
36                 lock (consumer1)
37                 {
38                     Console.WriteLine("Proc->Lock consumer2->Lock consumer1 ");
39                 }
40             }
41 
42         }
43 
44         private static void Proc()
45         {
46             lock (consumer1)
47             {
48                 Console.WriteLine("Proc->Lock consumer1");
49                 Thread.Sleep(1000);
50                 lock (consumer2)
51                 {
52                     Console.WriteLine("Proc->Lock consumer1->Lock consumer2 ");
53                 }
54             }
55 
56         }
57     }      

運作程式,便進入死鎖。

ok,上 windbg.

.load C:\Symbols\sosex_64\sosex.dll

0:000> !dlk

1 0:000> !dlk
 2 Examining SyncBlocks...
 3 Scanning for ReaderWriterLock instances...
 4 Scanning for holders of ReaderWriterLock locks...
 5 Scanning for ReaderWriterLockSlim instances...
 6 Scanning for holders of ReaderWriterLockSlim locks...
 7 Examining CriticalSections...
 8 Scanning for threads waiting on SyncBlocks...
 9 *** WARNING: Unable to verify checksum for D:\Test\PInvoke\CPP\Test\bin\Debug\Test.exe
10 Scanning for threads waiting on ReaderWriterLock locks...
11 Scanning for threads waiting on ReaderWriterLocksSlim locks...
12 Scanning for threads waiting on CriticalSections...
13 *DEADLOCK DETECTED*
14 CLR thread 0x3 holds the lock on SyncBlock 0000000000c94690 OBJ:00000000027736b8[Test.Consumer1]
15 ...and is waiting for the lock on SyncBlock 0000000000c946d8 OBJ:00000000027736d0[Test.Consumer2]
16 CLR thread 0x1 holds the lock on SyncBlock 0000000000c946d8 OBJ:00000000027736d0[Test.Consumer2]
17 ...and is waiting for the lock on SyncBlock 0000000000c94690 OBJ:00000000027736b8[Test.Consumer1]
18 CLR Thread 0x3 is waiting at Test.Program.Proc()(+0x31 IL,+0x98 Native) [D:\Test\PInvoke\CPP\Test\Program.cs @ 60,17]
19 CLR Thread 0x1 is waiting at Test.Program.Main(System.String[])(+0x68 IL,+0x196 Native) [D:\Test\PInvoke\CPP\Test\Program.cs @ 46,17]
20 
21 
22 1 deadlock detected.      

隻需敲一個指令,死鎖就檢測到了。 注意下面這些位址:

0000000000c94690 OBJ:00000000027736b8
      
0000000000c946d8 OBJ:00000000027736d0
      
0000000000c946d8 OBJ:00000000027736d0      
0000000000c94690 OBJ:00000000027736b8

!mdt 00000000027736b8 看下,把這四個位址都看下:
      
1 0:000> !mdt 00000000027736b8
 2 00000000027736b8 (Test.Consumer1)
 3     connString:00000000027735b8 (System.String) Length=5, String="Conn1"
 4 0:000> !mdt 00000000027736d0
 5 00000000027736d0 (Test.Consumer2)
 6     connString:00000000027735e0 (System.String) Length=5, String="Conn2"
 7 0:000> !mdt 00000000027736d0
 8 00000000027736d0 (Test.Consumer2)
 9     connString:00000000027735e0 (System.String) Length=5, String="Conn2"
10 0:000> !mdt 00000000027736b8
11 00000000027736b8 (Test.Consumer1)
12     connString:00000000027735b8 (System.String) Length=5, String="Conn1"      
明了了。      
對比下上面那個程式,瞧一瞧,是不是這幾個對像,連對象裡的字元串值都一覽無餘!


      

繼續閱讀