1 using System; 2 using System.Runtime.InteropServices; 3 4 namespace Example_13_1_1 5 { 6 internal class Program 7 { 8 [DllImport("Example_13_1_1_2.dll",CallingConvention =CallingConvention.Cdecl,CharSet =CharSet.Unicode)] 9 public extern static int InitChars(char[] chars); 10 11 static void Main(string[] args) 12 { 13 char[] c = { 'a', 'b', 'c'}; 14 var len = InitChars(c); 15 16 Console.WriteLine($"len={len}"); 17 Console.Read(); 18 } 19 } 20 }
第二部分,我還使用 Visual Studio 2022 建立了一個 C++ 專案,專案名:Example_13_1_1_2,Example_13_1_1_2.cpp 原始碼如下:
1 // Example_13_1_1_2.cpp : 此檔案包含 "main" 函數。程式執行將在此處開始並結束。 2 // 3 4 extern "C" 5 { 6 _declspec(dllexport) int InitChars(wchar_t c[]); 7 } 8 9 #include <iostream> 10 using namespace std; 11 12 int InitChars(wchar_t* c) 13 { 14 for (size_t i = 0; i < 100; i++) 15 { 16 c[i] = L'a'; 17 } 18 return sizeof(wchar_t) * 100; 19 } 20 21 //int InitChars(wchar_t c[]) 22 //{ 23 // for (size_t i = 0; i < 100; i++) 24 // { 25 // c[i] = L'a'; 26 // } 27 // return sizeof(wchar_t) * 100; 28 //}
1 <?xml version="1.0" encoding="utf-8" ?> 2 <mdaConfig> 3 <assistants> 4 <gcManagedToUnmanaged/> 5 <gcUnmanagedToManaged/> 6 </assistants> 7 </mdaConfig>
1 using System.Diagnostics; 2 3 namespace Example_13_1_2 4 { 5 internal class Program 6 { 7 public static List<byte[]?> list = new List<byte[]?>(); 8 9 static void Main(string[] args) 10 { 11 Alloc(); 12 13 Console.WriteLine("2G 資料分配完畢,請觀察"); 14 Console.ReadLine(); 15 16 GC.Collect(); 17 Console.WriteLine("碎片化已經產生,請觀察"); 18 19 Debugger.Break(); 20 } 21 22 private static void Alloc() 23 { 24 for (int i = 0; i < 1000; i++) 25 { 26 var byteLength = 1024 * 1024 * (i % 2 == 0 ? 2 : 1); 27 list.Add(new byte[byteLength]); 28 29 if (i % 2 == 0) 30 { 31 list[i] = null; 32 } 33 } 34 } 35 } 36 }
1 using System.Linq; 2 using System; 3 using System.IO; 4 using System.Xml.Serialization; 5 using System.Xml; 6 7 namespace Example_13_1_3 8 { 9 internal class Program 10 { 11 static void Main(string[] args) 12 { 13 var xml = @"<FabrikamCustomer><Id>001</Id><FirstName>John</FirstName><LastName>Dow</LastName></FabrikamCustomer>"; 14 15 Enumerable.Range(0, 30000) 16 .Select(i => GetCustomer(i, "FabrikamCustomer", xml)) 17 .ToList(); 18 19 Console.WriteLine("處理完成!"); 20 Console.ReadLine(); 21 } 22 23 public static Customer GetCustomer(int i, string rootElementName, string xml) 24 { 25 var xmlSerializer = new XmlSerializer(typeof(Customer), new XmlRootAttribute(rootElementName)); 26 using (var textReader = new StringReader(xml)) 27 { 28 using (var xmlReader = XmlReader.Create(textReader)) 29 { 30 Console.WriteLine(i); 31 32 return (Customer)xmlSerializer.Deserialize(xmlReader); 33 } 34 } 35 } 36 } 37 38 public class Customer 39 { 40 public string Id { get; set; } 41 public string FirstName { get; set; } 42 public string LastName { get; set; } 43 } 44 }
COMPLUS_MDA=1,啟動 MDA.
如圖:
第二步:我為我的程式設定了 mda 組態檔,檔名:Example_13_1_1.exe.mda.config。設定詳情如下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <mdaConfig> 3 <assistants> 4 <gcManagedToUnmanaged/> 5 <gcUnmanagedToManaged/> 6 </assistants> 7 </mdaConfig>
第三步:我沒有選擇偵錯工具,直接執行 exe,你就能看到有錯誤了,否則,錯誤很難發現的。效果如圖:
其實,只要我們啟動了 MDA,使用 Visual Studio 也是可以檢測的,VS就會及時丟擲異常,如圖:
以上是解決問題的方法,等程式出錯,能夠及時通知我們。
接下來,我們通過 Windbg 檢視一下,做到眼見為實,看看記憶體被修改是什麼樣子。
我們需要使用【g】命令,繼續執行程式,我們可以看到 Windbg 捕獲到了異常。
1 0:000> g 2 ModLoad: 00007ffb`7ed00000 00007ffb`7edaa000 C:\Windows\System32\ADVAPI32.dll 3 ModLoad: 00007ffb`7f020000 00007ffb`7f0be000 C:\Windows\System32\msvcrt.dll 4 ModLoad: 00007ffb`7d9d0000 00007ffb`7da6b000 C:\Windows\System32\sechost.dll 5 ModLoad: 00007ffb`7ee10000 00007ffb`7ef33000 C:\Windows\System32\RPCRT4.dll 6 ModLoad: 00007ffb`65540000 00007ffb`655ea000 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscoreei.dll 7 ModLoad: 00007ffb`7f3f0000 00007ffb`7f445000 C:\Windows\System32\SHLWAPI.dll 8 ModLoad: 00007ffb`7ca70000 00007ffb`7ca83000 C:\Windows\System32\kernel.appcore.dll 9 ModLoad: 00007ffb`7c460000 00007ffb`7c46a000 C:\Windows\SYSTEM32\VERSION.dll 10 ModLoad: 00007ffb`5eeb0000 00007ffb`5f972000 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll 11 ModLoad: 00007ffb`7db20000 00007ffb`7dcc0000 C:\Windows\System32\USER32.dll 12 ModLoad: 00007ffb`7cb70000 00007ffb`7cb92000 C:\Windows\System32\win32u.dll 13 ModLoad: 00007ffb`66830000 00007ffb`66846000 C:\Windows\SYSTEM32\VCRUNTIME140_CLR0400.dll 14 ModLoad: 00007ffb`5e9b0000 00007ffb`5ea6d000 C:\Windows\SYSTEM32\ucrtbase_clr0400.dll 15 ModLoad: 00007ffb`7d540000 00007ffb`7d56a000 C:\Windows\System32\GDI32.dll 16 ModLoad: 00007ffb`7cc60000 00007ffb`7cd6a000 C:\Windows\System32\gdi32full.dll 17 ModLoad: 00007ffb`7cd70000 00007ffb`7ce0d000 C:\Windows\System32\msvcp_win.dll 18 ModLoad: 00007ffb`7ce10000 00007ffb`7cf10000 C:\Windows\System32\ucrtbase.dll 19 ModLoad: 00007ffb`7e400000 00007ffb`7e430000 C:\Windows\System32\IMM32.DLL 20 ModLoad: 00007ffb`7d570000 00007ffb`7d8c4000 C:\Windows\System32\combase.dll 21 (3694.300c): Unknown exception - code 04242420 (first chance) 22 ModLoad: 00007ffb`5be50000 00007ffb`5d450000 C:\Windows\assembly\\mscorlib.ni.dll 23 ModLoad: 00007ffb`7dcc0000 00007ffb`7dde9000 C:\Windows\System32\ole32.dll 24 ModLoad: 00007ffb`7d570000 00007ffb`7d8c4000 C:\Windows\System32\combase.dll 25 ModLoad: 00007ffb`7c9f0000 00007ffb`7ca6f000 C:\Windows\System32\bcryptPrimitives.dll 26 ModLoad: 00007ffb`5b680000 00007ffb`5b7cf000 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clrjit.dll 27 ModLoad: 00007ffb`6b170000 00007ffb`6b195000 E:\Visual Studio 2022\Example_13_1_1\bin\Debug\Example_13_1_1_2.dll 28 ModLoad: 00007ffb`6b140000 00007ffb`6b16e000 C:\Windows\SYSTEM32\VCRUNTIME140D.dll 29 ModLoad: 00007ffb`0ffa0000 00007ffb`101bf000 C:\Windows\SYSTEM32\ucrtbased.dll 30 (3694.300c): Access violation - code c0000005 (first chance) 31 First chance exceptions are reported before any exception handling. 32 This exception may be expected and handled. 33 clr!WKS::gc_heap::mark_phase+0x73: 34 00007ffb`5ef1bd4d 393b cmp dword ptr [rbx],edi ds:00610061`00610060=????????
紅色標註的就是發生了異常。發生了異常,我們看看當前執行緒的執行緒棧。
1 0:000> !clrstack -l 2 OS Thread Id: 0x300c (0) 3 Child SP IP Call Site 4 00000011dbb6eda8 00007ffb5ef1bd4d [HelperMethodFrame: 00000011dbb6eda8] System.StubHelpers.StubHelpers.TriggerGCForMDA() 5 00000011dbb6eeb8 00007ffaff970ad6 [InlinedCallFrame: 00000011dbb6eeb8] Example_13_1_1.Program.InitChars(Char[]) 6 00000011dbb6ee90 00007ffaff970ad6 DomainBoundILStubClass.IL_STUB_PInvoke(Char[]) 7 8 00000011dbb6ef90 00007ffaff97090b Example_13_1_1.Program.Main(System.String[]) [E:\Visual Studio 2022\Example_13_1_1\Program.cs @ 14] 9 LOCALS: 10 0x00000011dbb6efd0 = 0x000001fb00002ed0 11 0x00000011dbb6efec = 0x0000000000000000 12 13 00000011dbb6f1f8 00007ffb5eeb6913 [GCFrame: 00000011dbb6f1f8]
紅色標記的地址"0x000001fb00002ed0"就是char[],我們可以使用【!dumpobj /d 0x000001fb00002ed0】命令確認一下。
1 0:000> !dumpobj /d 0x000001fb00002ed0 2 Name: System.Char[] 這就是我們的陣列,我們可以使用dp 命令檢視他的內容。 3 MethodTable: 00007ffb5be767d0 4 EEClass: 00007ffb5bfe5870 5 Size: 30(0x1e) bytes 6 Array: Rank 1, Number of elements 3, Type Char (Print Array) 7 Content: aaa 8 Fields: 9 None
我們使用【dp 000001fb00002ed0】命令,檢視一下,0061 就是 a ,這麼多 a 就是被 C++ 該寫的。
1 0:000> dp 000001fb00002ed0 2 000001fb`00002ed0 00007ffb`5be767d0 00000000`00000003 3 000001fb`00002ee0 00610061`00610061 00610061`00610061 4 000001fb`00002ef0 00610061`00610061 00610061`00610061 5 000001fb`00002f00 00610061`00610061 00610061`00610061 6 000001fb`00002f10 00610061`00610061 00610061`00610061 7 000001fb`00002f20 00610061`00610061 00610061`00610061 8 000001fb`00002f30 00610061`00610061 00610061`00610061 9 000001fb`00002f40 00610061`00610061 00610061`00610061
我們也可以使用【?】檢視一下 0061 的值,是十進位制的 97,97對應的字母就是 a。
1 0:000> ? 0061 2 Evaluate expression: 97 = 00000000`00000061
00007ffb`5be767d0 這個地址就是方發表。我們可以使用【!dumpmt 】命令檢視一下。
1 0:000> !dumpmt 00007ffb`5be767d0 2 EEClass: 00007ffb5bfe5870 3 Module: 00007ffb5be51000 4 Name: System.Char[] 5 mdToken: 0000000002000000 6 File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll 7 BaseSize: 0x18 8 ComponentSize: 0x2 9 Slots in VTable: 28 10 Number of IFaces in IFaceMap: 6
00000000`00000003 這個值就是陣列的長度,char[] c = { 'a', 'b', 'c'},這個陣列有3個元素。我們應該只有3個 00610061`00610061,但是這裡卻有很多,因為被 C++ 程式覆寫了。這個測試程式雖然沒有出錯,可能是GC 還不知道,或者還沒有被其他使用,如果一有使用,就會有想不到的錯誤,如果我們不使用 MDA,這種錯誤是很難發現的。
1 0:001> !eeheap -gc 2 3 ======================================== 4 Number of GC Heaps: 1 5 ---------------------------------------- 6 Small object heap 7 segment begin allocated committed allocated size committed size 8 generation 0: 9 02cbf425f320 028be2c00020 028be2c0e1f0 028be2c11000 0xe1d0 (57808) 0x11000 (69632) 10 generation 1: 11 02cbf425f1c0 028be2400020 028be2409fb8 028be2411000 0x9f98 (40856) 0x11000 (69632) 12 generation 2: 13 02cbf425f950 028be5000020 028be5000020 028be5001000 0x1000 (4096) 14 Large object heap 15 segment begin allocated committed allocated size committed size 16 02cbf425f3d0 028be3000020 028be4f004b8 028be4f01000 0x1f00498 (32507032) 0x1f01000 (32509952) 17 02cbf425fa00 028be5400020 028be7300480 028be7301000 0x1f00460 (32506976) 0x1f01000 (32509952) 18 02cbf425ff80 028be7400020 028be93004b8 028be9301000 0x1f00498 (32507032) 0x1f01000 (32509952) 19 02cbf4260500 028be9400020 028beb100448 028beb121000 0x1d00428 (30409768) 0x1d21000 (30543872) 20 02cbf4260a80 028beb400020 028bed3004b8 028bed301000 0x1f00498 (32507032) 0x1f01000 (32509952) 21 02cbf4261000 028bed400020 028bef3004b8 028bef301000 0x1f00498 (32507032) 0x1f01000 (32509952) 22 02cbf4261580 028bef400020 028bf13004b8 028bf1301000 0x1f00498 (32507032) 0x1f01000 (32509952) 23 02cbf4261b00 028bf1400020 028bf33004b8 028bf3301000 0x1f00498 (32507032) 0x1f01000 (32509952) 24 02cbf4262080 028bf3400020 028bf53004b8 028bf5301000 0x1f00498 (32507032) 0x1f01000 (32509952) 25 02cbf4262600 028bf5400020 028bf7100448 028bf7121000 0x1d00428 (30409768) 0x1d21000 (30543872) 26 02cbf4262b80 028bf7400020 028bf93004b8 028bf9301000 0x1f00498 (32507032) 0x1f01000 (32509952) 27 02cbf4263100 028bf9400020 028bfb3004b8 028bfb301000 0x1f00498 (32507032) 0x1f01000 (32509952) 28 02cbf4263680 028bfb400020 028bfd100448 028bfd121000 0x1d00428 (30409768) 0x1d21000 (30543872) 29 02cbf4263c00 028bfd400020 028bff3004b8 028bff301000 0x1f00498 (32507032) 0x1f01000 (32509952) 30 02cbf4264180 028bff400020 028c013004b8 028c01301000 0x1f00498 (32507032) 0x1f01000 (32509952) 31 02cbf4264700 028c01400020 028c03100448 028c03121000 0x1d00428 (30409768) 0x1d21000 (30543872) 32 02cbf4264c80 028c03400020 028c053004b8 028c05301000 0x1f00498 (32507032) 0x1f01000 (32509952) 33 02cbf4265200 028c05400020 028c073004b8 028c07301000 0x1f00498 (32507032) 0x1f01000 (32509952) 34 02cbf4265780 028c07400020 028c09100448 028c09121000 0x1d00428 (30409768) 0x1d21000 (30543872) 35 02cbf4265d00 028c09400020 028c0b3004b8 028c0b301000 0x1f00498 (32507032) 0x1f01000 (32509952) 36 02cbf4266280 028c0b400020 028c0d3004b8 028c0d301000 0x1f00498 (32507032) 0x1f01000 (32509952) 37 02cbf4266800 028c0d400020 028c0f100448 028c0f121000 0x1d00428 (30409768) 0x1d21000 (30543872) 38 02cbf4266d80 028c0f400020 028c113004b8 028c11301000 0x1f00498 (32507032) 0x1f01000 (32509952) 39 02cbf4267300 028c11400020 028c133004b8 028c13301000 0x1f00498 (32507032) 0x1f01000 (32509952) 40 02cbf4267880 028c13400020 028c15100448 028c15121000 0x1d00428 (30409768) 0x1d21000 (30543872) 41 02cbf4267e00 028c15400020 028c173004b8 028c17301000 0x1f00498 (32507032) 0x1f01000 (32509952) 42 02cbf4268380 028c17400020 028c193004b8 028c19301000 0x1f00498 (32507032) 0x1f01000 (32509952) 43 02cbf4268900 028c19400020 028c1b100448 028c1b121000 0x1d00428 (30409768) 0x1d21000 (30543872) 44 02cbf4268e80 028c1b400020 028c1c7002f8 028c1c721000 0x13002d8 (19923672) 0x1321000 (20058112) 45 Pinned object heap 46 segment begin allocated committed allocated size committed size 47 02cbf425ec40 028be0400020 028be0404428 028be0411000 0x4408 (17416) 0x11000 (69632) 48 ------------------------------ 49 GC Allocated Heap Size: Size: 0x36724530 (913458480) bytes. 50 GC Committed Heap Size: Size: 0x36871000 (914821120) bytes.
我們可以看到,大物件堆有很多東西。我們隨便找一個 Segment 檢視一下具體的情況。我選擇最後一個,紅色標註的地址範圍。
1 0:001> !dumpheap 028c1b400020 028c1c7002f8 2 Address MT Size 3 028c1b400020 028bde5b2910 32 Free 4 028c1b400040 7ffa9f2ac4a0 1,048,600 5 028c1b500058 028bde5b2910 2,097,240 Free 6 028c1b7000b0 7ffa9f2ac4a0 1,048,600 7 028c1b8000c8 028bde5b2910 2,097,240 Free 8 028c1ba00120 7ffa9f2ac4a0 1,048,600 9 028c1bb00138 028bde5b2910 2,097,240 Free 10 028c1bd00190 7ffa9f2ac4a0 1,048,600 11 028c1be001a8 028bde5b2910 2,097,240 Free 12 028c1c000200 7ffa9f2ac4a0 1,048,600 13 028c1c100218 028bde5b2910 2,097,240 Free 14 028c1c300270 7ffa9f2ac4a0 1,048,600 15 028c1c400288 028bde5b2910 2,097,240 Free 16 028c1c6002e0 7ffa9f2ac4a0 1,048,600 17 18 Statistics: 19 MT Count TotalSize Class Name 20 7ffa9f2ac4a0 7 7,340,200 System.Byte[] 21 028bde5b2910 7 12,583,472 Free 22 Total 14 objects, 19,923,672 bytes
我們可以看到,隔一個物件就是一個 Free 塊,佔據的記憶體還不小。我們選擇一個 Free 塊,檢視一下它的前面和後面到底是什麼東西。
1 0:001> !gcroot 028c1bd00190 2 HandleTable: 3 0000028bdfeb13e8 (strong handle) 4 -> 028be0400020 System.Object[] 5 -> 028be2409f48 System.Collections.Generic.List<System.Byte[]> 6 -> 028be2c021a8 System.Byte[][] 7 -> 028c1bd00190 System.Byte[] 8 9 Found 1 unique roots.
我們在使用【!gcroot 028c1c000200】命令檢視「028c1c000200」的跟參照。
1 0:001> !gcroot 028c1c000200 2 HandleTable: 3 0000028bdfeb13e8 (strong handle) 4 -> 028be0400020 System.Object[] 5 -> 028be2409f48 System.Collections.Generic.List<System.Byte[]> 6 -> 028be2c021a8 System.Byte[][] 7 -> 028c1c000200 System.Byte[] 8 9 Found 1 unique roots.
1 0:001> !do 028be2409f48 2 Name: System.Collections.Generic.List`1[[System.Byte[], System.Private.CoreLib]] 3 MethodTable: 00007ffa9f2ac620 4 EEClass: 00007ffa9f291890 5 Tracked Type: false 6 Size: 32(0x20) bytes 7 File: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\7.0.14\System.Private.CoreLib.dll 8 Fields: 9 MT Field Offset Type VT Attr Value Name 10 00007ffa9f2ace40 400214c 8 System.__Canon[] 0 instance 0000028be2c021a8 _items(列表的資料項) 11 00007ffa9f11e8d8 400214d 10 System.Int32 1 instance 1000 _size 12 00007ffa9f11e8d8 400214e 14 System.Int32 1 instance 1500 _version 13 00007ffa9f2ace40 400214f 8 System.__Canon[] 0 static dynamic statics NYI s_emptyArray
我們可以繼續使用【!do 0000028be2c021a8】命令,檢視他的資料詳情。
1 0:001> !DumpObj /d 0000028be2c021a8 2 Name: System.Byte[][] 3 MethodTable: 00007ffa9f2acbe8 4 EEClass: 00007ffa9f2acb50 5 Tracked Type: false 6 Size: 8216(0x2018) bytes 7 Array: Rank 1, Number of elements 1024, Type SZARRAY (Print Array) 8 Fields: 9 None
說明 List<> 底層是用一個二維陣列實現的。當然,我們也可以使用【!da -details 0000028be2c021a8】命令檢視陣列詳情。
1 0:001> !da -details 0000028be2c021a8 2 Name: System.Byte[][] 3 MethodTable: 00007ffa9f2acbe8 4 EEClass: 00007ffa9f2acb50 5 Size: 8216(0x2018) bytes 6 Array: Rank 1, Number of elements 1024, Type SZARRAY 7 Element Methodtable: 00007ffa9f2ac4a0 8 [0] null 9 [1] 0000028be3200078 10 Name: System.Byte[] 11 MethodTable: 00007ffa9f2ac4a0 12 EEClass: 00007ffa9f2ac420 13 Tracked Type: false 14 Size: 1048600(0x100018) bytes 15 Array: Rank 1, Number of elements 1048576, Type Byte (Print Array) 16 Content: .................................................................................................................... 17 Fields: 18 None 19 [2] null 20 [3] 0000028be33000b0 21 Name: System.Byte[] 22 MethodTable: 00007ffa9f2ac4a0 23 EEClass: 00007ffa9f2ac420 24 Tracked Type: false 25 Size: 1048600(0x100018) bytes 26 Array: Rank 1, Number of elements 1048576, Type Byte (Print Array) 27 Content: .................................................................................................................... 28 Fields: 29 None 30 [4] null 31 .....還有太多內容,省略了。
1 0:006> !eeheap -loader 2 Loader Heap: 3 -------------------------------------- 4 System Domain: 6fa9caf8 5 LowFrequencyHeap: 00e20000(3000:3000) 062f0000(10000:2000) Size: 0x5000 (20480) bytes. 6 HighFrequencyHeap: 00e24000(9000:1000) Size: 0x1000 (4096) bytes. 7 StubHeap: 00e2d000(3000:1000) Size: 0x1000 (4096) bytes. 8 Virtual Call Stub Heap: 9 IndcellHeap: 00f60000(2000:1000) Size: 0x1000 (4096) bytes. 10 LookupHeap: 00f65000(2000:1000) Size: 0x1000 (4096) bytes. 11 ResolveHeap: 00f6b000(5000:1000) Size: 0x1000 (4096) bytes. 12 DispatchHeap: 00f67000(4000:1000) Size: 0x1000 (4096) bytes. 13 CacheEntryHeap: 00f62000(3000:1000) Size: 0x1000 (4096) bytes. 14 Total size: Size: 0xc000 (49152) bytes. 15 -------------------------------------- 16 Shared Domain: 6fa9c7a8 17 LowFrequencyHeap: 00e20000(3000:3000) 062f0000(10000:2000) Size: 0x5000 (20480) bytes. 18 HighFrequencyHeap: 00e24000(9000:1000) Size: 0x1000 (4096) bytes. 19 StubHeap: 00e2d000(3000:1000) Size: 0x1000 (4096) bytes. 20 Virtual Call Stub Heap: 21 IndcellHeap: 00f60000(2000:1000) Size: 0x1000 (4096) bytes. 22 LookupHeap: 00f65000(2000:1000) Size: 0x1000 (4096) bytes. 23 ResolveHeap: 00f6b000(5000:1000) Size: 0x1000 (4096) bytes. 24 DispatchHeap: 00f67000(4000:1000) Size: 0x1000 (4096) bytes. 25 CacheEntryHeap: 00f62000(3000:1000) Size: 0x1000 (4096) bytes. 26 Total size: Size: 0xc000 (49152) bytes. 27 -------------------------------------- 28 Domain 1: 00c1d890 29 LowFrequencyHeap: 00f40000(3000:3000) 00ff0000(10000:10000) 04f60000(10000:10000) 05080000(10000:10000) 05090000(10000:10000) 050a0000(10000:10000) 052d0000(10000:10000) 052e0000(10000:10000) 052f0000(10000:10000) 05300000(10000:10000) 05320000(10000:10000) 05340000(10000:10000) 05750000(10000:10000) 05770000(10000:10000) 05780000(10000:10000) 057a0000(10000:10000) 057b0000(10000:10000) 057d0000(10000:10000) 057e0000(10000:10000) 05800000(10000:10000) 05820000(10000:10000) 05830000(10000:10000) 05840000(10000:10000) 05850000(10000:10000) 05880000(10000:10000) 06090000(10000:10000) 060a0000(10000:10000) 060d0000(10000:10000) 060e0000(10000:10000) 060f0000(10000:10000) 06100000(10000:10000) 06120000(10000:10000) 06130000(10000:10000) 06150000(10000:10000) 06170000(10000:10000) 06180000(10000:10000) 06190000(10000:10000) 061b0000(10000:10000) 061d0000(10000:10000) 061e0000(10000:10000) 061f0000(10000:10000) 06220000(10000:10000) 06230000(10000:10000) 06240000(10000:10000) 06250000(10000:10000) 06280000(10000:10000) 06290000(10000:10000) 062a0000(10000:10000) 062c0000(10000:10000) 062d0000(10000:10000) 072d0000(10000:10000) 072e0000(10000:10000) 07300000(10000:10000) 07320000(10000:10000) 07330000(10000:10000) 07350000(10000:10000) 07360000(10000:10000) 07370000(10000:10000) 07380000(10000:10000) 073b0000(10000:10000) 073c0000(10000:10000) 073d0000(10000:10000) 07400000(10000:10000) 07410000(10000:10000) 07420000(10000:10000) 07430000(10000:10000) 07450000(10000:10000) 07470000(10000:10000) 07480000(10000:10000) 074a0000(10000:10000) 074b0000(10000:5000) Size: 0x458000 (4554752) bytes. 30 HighFrequencyHeap: 00f43000(a000:a000) 04f50000(10000:10000) 050b0000(10000:10000) 05310000(10000:10000) 05760000(10000:10000) 057c0000(10000:10000) 05810000(10000:10000) 05870000(10000:10000) 060b0000(10000:10000) 06110000(10000:10000) 06160000(10000:10000) 061c0000(10000:10000) 06210000(10000:10000) 06270000(10000:10000) 062b0000(10000:10000) 072f0000(10000:10000) 07340000(10000:10000) 073a0000(10000:10000) 073e0000(10000:10000) 07440000(10000:10000) 07490000(10000:7000) Size: 0x141000 (1314816) bytes. 31 StubHeap: Size: 0x0 (0) bytes. 32 Virtual Call Stub Heap: 33 IndcellHeap: 00f50000(2000:1000) Size: 0x1000 (4096) bytes. 34 LookupHeap: 00f56000(1000:1000) Size: 0x1000 (4096) bytes. 35 ResolveHeap: 00f5a000(6000:2000) Size: 0x2000 (8192) bytes. 36 DispatchHeap: 00f57000(3000:1000) Size: 0x1000 (4096) bytes. 37 CacheEntryHeap: 00f52000(4000:1000) Size: 0x1000 (4096) bytes. 38 Total size: Size: 0x59f000 (5894144) bytes. 39 -------------------------------------- 40 Jit code heap: 41 LoaderCodeHeap: 00000000(0:0) Size: 0x0 (0) bytes. 42 LoaderCodeHeap: 00000000(0:0) Size: 0x0 (0) bytes. 43 LoaderCodeHeap: 00000000(0:0) Size: 0x0 (0) bytes. 44 LoaderCodeHeap: 00000000(0:0) Size: 0x0 (0) bytes. 45 LoaderCodeHeap: 00000000(0:0) Size: 0x0 (0) bytes. 46 LoaderCodeHeap: 00000000(0:0) Size: 0x0 (0) bytes. 47 ...... 48 Module 0744c80c: Size: 0x0 (0) bytes. 49 Module 0744cfa4: Size: 0x0 (0) bytes. 50 Module 0744d73c: Size: 0x0 (0) bytes. 51 Module 0744ded4: Size: 0x0 (0) bytes. 52 Module 0744e66c: Size: 0x0 (0) bytes. 53 Module 0744ee04: Size: 0x0 (0) bytes. 54 Module 0744f59c: Size: 0x0 (0) bytes. 55 Module 07490010: Size: 0x0 (0) bytes. 56 Module 074907a4: Size: 0x0 (0) bytes. 57 Module 07490f3c: Size: 0x0 (0) bytes. 58 Module 074916d4: Size: 0x0 (0) bytes. 59 Module 07491e6c: Size: 0x0 (0) bytes. 60 Module 07492604: Size: 0x0 (0) bytes. 61 Module 07492d9c: Size: 0x0 (0) bytes. 62 Module 07493534: Size: 0x0 (0) bytes. 63 Module 07493ccc: Size: 0x0 (0) bytes. 64 Module 07494464: Size: 0x0 (0) bytes. 65 Module 07494bfc: Size: 0x0 (0) bytes. 66 Module 07495394: Size: 0x0 (0) bytes. 67 Module 07495b2c: Size: 0x0 (0) bytes. 68 Total size: Size: 0x0 (0) bytes. 69 -------------------------------------- 70 Total LoaderHeap size: Size: 0x5b7000 (5992448) bytes. 71 =======================================
我們發現在【Loader Heap(載入堆)】裡有很多 Module,我們隨便選擇一個 Module 檢視,可以使用【!dumpModule】命令。
1 0:006> !DumpModule /d 07495b2c 2 Name: Unknown Module 3 Attributes: Reflection 4 Assembly: 069a70c0 5 LoaderHeap: 00000000 6 TypeDefToMethodTableMap: 074b33d4 7 TypeRefToMethodTableMap: 074b33e8 8 MethodDefToDescMap: 074b33fc 9 FieldDefToDescMap: 074b3424 10 MemberRefToDescMap: 00000000 11 FileReferencesMap: 074b3474 12 AssemblyReferencesMap: 074b3488
我們可以繼續檢視一下這個 Module 裡面有多少 MT(方發表),有了 MT 就可以知道 MD(方法描述符),我們就能瞭解什麼方法在起作用。
1 0:006> !dumpmt -md 07496260 2 EEClass: 074b4958 3 Module: 07495b2c 4 Name: <Unloaded Type> 5 mdToken: 02000006 6 File: Unknown Module 7 BaseSize: 0x14 8 ComponentSize: 0x0 9 Slots in VTable: 12 10 Number of IFaces in IFaceMap: 0 11 -------------------------------------- 12 MethodDesc Table 13 Entry MethodDe JIT Name 14 6e1e97b8 6ddec838 PreJIT System.Object.ToString() 15 6e1e96a0 6df28978 PreJIT System.Object.Equals(System.Object) 16 6e1f21f0 6df28998 PreJIT System.Object.GetHashCode() 17 6e1a4f2c 6df289a0 PreJIT System.Object.Finalize() 18 074c0e60 07496214 JIT Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract.get_Reader() 19 0746f6ed 0749621c NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract.get_Writer() 20 0746f6f1 07496224 NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract.get_ReadMethods() 21 0746f6f5 0749622c NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract.get_WriteMethods() 22 0746f6f9 07496234 NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract.get_TypedSerializers() 23 0746f6fd 0749623c NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract.CanSerialize(System.Type) 24 0746f701 07496244 NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract.GetSerializer(System.Type) 25 074c0e40 0749624c JIT Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract..ctor()
到這裡,我們就看到了,這麼多 Module 都有 GeneratedAssembly.XmlSerializerContract..ctor(),說明這就是問題所在。
我們點選【collect】選單,選擇【collect】子選單,開啟資料收集的視窗,重要操作我已經使用紅色標記了。
Additional Providers的值,然後加上記錄棧的key,即 @StacksEnabled=true
,合併後就是::Microsoft-Windows-DotNETRuntime:LoaderKeyword:Always:@StacksEnabled=true
1 Microsoft-Windows-DotNETRuntime:LoaderKeyword:Always:@StacksEnabled=true
這個值是需要設定的,我們可以點開【Provider Browser】按鈕進行設定。效果如圖:
設定好以後,我們就可以點選【Start Collection】開始收集資料了,效果如圖:
Prefview 開始收集資料以後,我們開啟我們的專案程式,也就是 exe 程式,直接執行。我等程式執行到2000左右就關閉程式,同時也點選【Stop Collection】停止 Perfview 的收集。我們需要等待一下,Perfview 執行完畢,生成zip 檔案,效果如圖:
我們雙擊【Events】開啟了事件視窗,在彈框中搜尋 AssemblyLoad 事件,然後在【Time MSec】 列點選右鍵選擇 【Open Any Stacks】 開啟此次載入的 執行緒呼叫棧, 如下圖所示:
右鍵【Open Any Stacks】開啟一個【Any Stacks】新視窗,我們就可以檢視詳情了,效果如圖:
好了,這個過程終於完了,挺困難的,這個過程我搞了好多遍才有說收穫。