写本文主要两个目的,一个是解析下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格式显式出来的,可能不是很好辨认,下面换一种内存