.Net 2.0內存對象布局詳析及與1.1變化比較
寫本文主要兩個目的,一個是解析下2.0下的對象在內存里面究盡是個什么樣子的布局,使用windbg和sos來show下內存里面的bit是如何組織其來的。另外一個就是比較下和.Net Framework 1.1你面的內存布局有什么區別,修正下“Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects”里面的很多說法在Framework 2.0下面的不同的地方。
好久以前整的東西,這會給記錄下來。表笑話,現在都
首先找個小的C#的演示程序:
namespace CLRLayoutTest2._0
{
class Program
{
static int TestStaticFields = 1221119;
static object TestStaticObject = new object();
static string TestStaticMethod()
{
return "Test Static Method";
}
static void Main(string[] args)
{
Program a = new Program();
a.Test();
System.Console.ReadLine();
}
public void Test()
{
int i = 2;
object testObject = (object)i;
System.Console.WriteLine(testObject.ToString());
System.Console.WriteLine(TestStaticFields.ToString());
}
}
}
好,設置好在.Net Framework 2.0的環境下給編譯了。然后設置好windbg的調試環境,接著給attach上去:
0:000> !dso
OS Thread Id: 0x79c (0)
ESP/REG Object Name
0012f3c4 013f37b8 Microsoft.Win32.SafeHandles.SafeFileHandle
0012f3d4 013f37b8 Microsoft.Win32.SafeHandles.SafeFileHandle
0012f408 013f3854 System.Byte[]
0012f40c 013f37cc System.IO.__ConsoleStream
0012f430 013f37fc System.IO.StreamReader
0012f434 013f37fc System.IO.StreamReader
0012f438 013f16e8 CLRLayoutTest2._0.Program
0012f448 013f37fc System.IO.StreamReader
0012f44c 013f3b70 System.IO.TextReader+SyncTextReader
0012f450 013f16e8 CLRLayoutTest2._0.Program
0012f460 013f3b70 System.IO.TextReader+SyncTextReader
0012f464 013f16e8 CLRLayoutTest2._0.Program
0012f46c 013f16e8 CLRLayoutTest2._0.Program
0012f478 013f16cc System.Object[] (System.String[])
0012f534 013f16cc System.Object[] (System.String[])
0012f6e0 013f16cc System.Object[] (System.String[])
0012f708 013f16cc System.Object[] (System.String[])
看到咱的Program了,接著就看看013f16e8里面都有些啥呢,
0:000> !objsize 013f16e8
sizeof(013f16e8) = 12 (0xc) bytes (CLRLayoutTest2._0.Program)
好吧,12個字節,lesgo,查看memory:
013f16e8 0000000000a83060 7910229000000000
013f16f8 0000000000000002 00000000790fd0f0
給標出來的部分,就是CLRLayoutTest2._0.Program這個type的一個instance object在內存里面的內容。
這個地方,稍微再說說一個object instance在內存你面的結構,在以前的文章里面有詳細說過這個東西的。一個instance的前2個字節,保存的是SyncBlk,這個是和同步相關的東西,其實現原理和lock是一樣的,flier曾經寫過文章講SyncBlk的實現原理和過程,感興趣的可以去看看。接著下來的2個字節,保存的就是type handle,也就是我們經常說的obj ref,對一個對象的引用其實就是指到這個的方的。這個例子里面,顯示的是00a83060。
我們可以來驗證下:
0:000> !do 013f16e8
Name: CLRLayoutTest2._0.Program
MethodTable: 00a83060
EEClass: 00a811f4
Size: 12(0xc) bytes
(E:\myProject\CLRLayoutTest2.0\CLRLayoutTest2.0\bin\Debug\CLRLayoutTest2.0.exe)
Fields:
MT Field Offset Type Attr Value Name
79102290 4000001 1c System.Int32 static 1221119 TestStaticFields
790fd0f0 4000002 4 System.Object static 013f16dc TestStaticObject
可以看到,這個對象的MethodTable: 00a83060,就是在object instance的數據空間的第5個字節開始起,四個字節存放的地方。79102290這個內存地址show的東西,就是這個type的instance的instance data。可以在上面的Fields區域里面看到,指類型變量,不包括ref type的數據的MT,都會在這個的方給show出來。0000000000000002,一個四個字節大小的空間,正是一個System.Int32的大小,
一個object在內存你面主要的東西,包括object instance,以及保存這個object重要結構的MethodTable,還有保存和object對應的type的EEClass。這三個東西,構成了內存里面一個object的主要內容。接下來看看主要數據結構MethodTable及其變化。
0:000> !dumpmt -md 00a83060
EEClass: 00a811f4
Module: 00a82c3c
Name: CLRLayoutTest2._0.Program
mdToken: 02000002 (E:\myProject\CLRLayoutTest2.0\CLRLayoutTest2.0\bin\Debug\CLRLayoutTest2.0.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 9
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79371278 7914b928 PreJIT System.Object.ToString()
7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)
7936b3d0 7914b948 PreJIT System.Object.GetHashCode()
793624d0 7914b950 PreJIT System.Object.Finalize()
00a8c011 00a83038 NONE CLRLayoutTest2._0.Program.TestStaticMethod()
00db00c0 00a83040 JIT CLRLayoutTest2._0.Program.Main(System.String[])
00db0148 00a83048 JIT CLRLayoutTest2._0.Program.Test()
00db0118 00a83050 JIT CLRLayoutTest2._0.Program..ctor()
00db0070 00a83058 JIT CLRLayoutTest2._0.Program..cctor()
上面是SOS解析好了內存之后列出的內存結構,使用quad Hex格式顯式出來的,可能不是很好辨認,下面換一種內存的現示格式,咱看看內存里面MethodTable是如何組織的:
00a83050 39030004 00000007 39040005 00200008
00a83060 00040000 0000000c 00090002 00000004
00a83070 790fd0f0 00a82c3c 00a830b0 00a811f4
00a83080 00000000 00000000 79371278 7936b3b0
00a83090 7936b3d0 793624d0 00a8c011 00db00c0
00a830a0 00db0148 00db0118 00db0070 00000000
00a830b0 00000080 00000000 00000000 00000000
首先,從-12個byte開始的的方看起,這個地方的數據是00000007 39040005 00200008,你面保存了和GC相關的信息。00a83060為MT的地址。
00040000占用四個字節,在定義的時候被定義成為一個DWORD,表示的是一個flag。這個flag的低位的那個word,也就是0004,表示的含義是array和string type在這個component里面的總和。我也不曉的怎么就算了個4出來。下面的一個0000000c表示的是這個class被分配到heap上面的時候的大小。這個大小只是計算了instance data的大小,其余的和clr相關的數據結構的大小并沒有包含進去,從上面也可以看到。
00090002,這個需要分成四個部分來解釋含義:00,09,00,02;00表示的是另外一個重要的標志位。其各個enum的值含義如下:
enum
{
enum_flag2_MarshaledByRef = 0x0001,
enum_flag2_NoSecurityProperties = 0x0002,
enum_flag2_HasGenericsStaticsInfo = 0x0004,
enum_flag2_MayNeedRestore = 0x0008,
enum_flag2_UNUSED = 0x0010,
enum_flag2_IsZapped = 0x0020,
enum_flag2_IsDynamicStatics = 0x0040,
enum_flag2_FixedAddressVTStatics = 0x0080,
enum_flag2_GenericsMask = 0x0300,
enum_flag2_GenericsMask_NonGeneric = 0x0000,
enum_flag2_GenericsMask_CanonInst = 0x0100,
enum_flag2_GenericsMask_NonCanonInst = 0x0200,
enum_flag2_GenericsMask_TypicalInst = 0x0300,
enum_flag2_ClassPreInited = 0x0400,
enum_flag2_IsAsyncPin = 0x0800,
enum_flag2_ContainsGenericVariables = 0x1000,
enum_flag2_IsInterface = 0x2000,
enum_flag2_HasDispatchMap = 0x4000,
enum_flag2_HasVariance = 0x8000,
};
09表示的是有9個方法。00表示虛方法0個,后面表示有2個接口。這個接口,應該是從父類繼承過來的。
這里出現的第一個變化,是在.Net Framework 1.1中,0000000c之后出現的應該是EEClass的地址。這里確變成了一段標識位。00000004的含義,推測不出來…
接下來的790fd0f0,表示的是static ref類型的地址。它的值,存放在013f16dc的地方。00a82c3c是module的地址。00a811f4保存的是EEClass的地址。
00000000 00000000表示numbers of vtable slots,這里沒有。
下面的79371278 7936b3b0 7936b3d0 793624d0 00a8c011 00db00c0 00db0148 00db0118 00db0070,表示的是9個方法的Entry入口。
這里還可以看到與1.1里面的一個變化,就是System.Object的幾個基本方法的入口地址的順序給改變了。
因為程序里面沒有涉及到static str和interface的定義,所以后面的內存地址中就沒數據了。
最后看看EEClass的數據結構:
0:000> !dumpclass 00a811f4
Class Name: CLRLayoutTest2._0.Program
mdToken: 02000002 (E:\myProject\CLRLayoutTest2.0\CLRLayoutTest2.0\bin\Debug\CLRLayoutTest2.0.exe)
Parent Class: 790fd08c
Module: 00a82c3c
Method Table: 00a83060
Vtable Slots: 4
Total Method Slots: 9
Class Attributes: 100000
NumInstanceFields: 0
NumStaticFields: 2
MT FieldOffset Type VT Attr Value Name
79102290 4000001 1c System.Int32 1 static 1221119 TestStaticFields
790fd0f0 4000002 4 System.Object 0 static 013f16dc TestStaticObject
Show下對應的地址空間里面的內容:
00a811f4 00000000 00a82c3c 02000002 00a83060
00a81204 00070008 00000012 00010002 00000000
00a81214 00000000 ffffffff ffffffff 00000000
00a81224 00a83010 00100000 02000100 00000000
最開始的00000000表示parent的EEClass的地址。這里不是從別的Class繼承過來的,所以是空的。00a82c3c是module的地址。02000002分成四段來解釋,分別表示number of interfaces,number of instance fields,number of staticfields和number of dumplicate solts。解釋的順序是00,02,02,00。00a83060是一個往回到MethodTable的連接,這樣可以方便程序往回找。下面的兩個dword,分別是EEClass里面不同類型的Fields的數量。
基本的結構就是這些,其實還有許多地方之間有關系的沒有給發掘出來。恩,還有許多不準確的地方,以后慢慢修改。這玩意太痿瑣了,MS隨便變一下,也不給個文檔,靠自個一個一個字節的扒拉太痛苦鳥。-_-
Wednesday, October 15, 2008 12:01:11 PM
First post at sscli.cnblogs.com
posted on 2008-10-15 12:04 lbq1221119 閱讀(3272) 評論(20) 收藏 舉報
浙公網安備 33010602011771號