.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 阅读(3260) 评论(20) 编辑 收藏 举报