每個虛擬機都有它自己的對象布局,本文我們將針對sscli源碼和windbg調(diào)試器來查看不同類型的.net對象布局。
在.net虛擬機里顶吮,每個對象都需要保存這些信息:
- 對象的類型;
- 對象實例的成員屬性(field)值肝谭;
- hash值、鎖信息等其他數(shù)據(jù)結(jié)構(gòu)蛾扇。
普通對象
在CLR里攘烛,對象在托管代碼(managed code)和非托管代碼(unmanaged code)里有不同的表現(xiàn)形式。在托管代碼里镀首,所有對象的基類Object類型是在 clr\src\bcl\system\Object.cs里定義坟漱,而其在非托管世界里,則復(fù)雜的多更哄,由在clr\src\vm\object.h里定義的Object類型芋齿,clr\src\vm\SyncBlk.h里定義的ObjHeader等類型實現(xiàn)。對象在非托管代碼理的內(nèi)存物理布局如下圖所示:
- 在非托管代碼里成翩,對象實際上有兩個指針觅捆。
- object指針就是虛擬機返回給托管代碼的對象引用地址,從這個指針開始麻敌,托管代碼就可以獲取到任何一個對象的類型以及成員變量信息栅炒。
- 而另外一個指針objhead,實際上是非托管代碼里术羔,每個.NET對象實際的指針赢赊,在這個指針后面,包含了控制線程同步级历,甚至是COM Interop相關(guān)信息的SyncBlock索引域携,這個索引的作用我們會在后文提到。因為索引只用到32位字節(jié)鱼喉,所以在64位系統(tǒng)運行的時候,會加上一個填充用的DWORD以便補齊內(nèi)存邊界趋观。
- object指針后面緊跟的就是該對象所屬的類型:MethodTable扛禽,MethodTable顧名思義就是函數(shù)表,在.NET里它就是對象類型的代表皱坛,在后文我們也會詳細說明它编曼。
- 類型信息后面就是每個對象實例成員變量的值信息了,如果是成員變量是引用類型剩辟,那么就保存被引用的對象的object指針(不是objhead)掐场,如果是值類型往扔,比如說結(jié)構(gòu)體,那么就按照值類型的內(nèi)存布局熊户,將變量值直接保存在對象的內(nèi)存區(qū)域里萍膛。
雖然.NET里所有引用類型的對象都從Object類型里繼承,一些特殊的對象嚷堡,在CLR里也在非托管代碼里定義了一份不同的布局蝗罗,方便虛擬機的處理。
數(shù)組對象 - Array
在托管代碼里蝌戒,其在 clr\src\bcl\system\Array.cs 里定義串塑,在非托管代碼里,其在 clr\src\vm\object.h 里定義北苟。之所以這樣做桩匪,是因為數(shù)組對象即可以保存引用類型,也可以保存值類型友鼻,而且為了方便程序訪問數(shù)組的長度傻昙,數(shù)組對象實際上是將長度信息直接保存在內(nèi)存里了,如下圖是其內(nèi)存布局:
- 除了將長度信息直接保存到內(nèi)存以外桃移,如果是多維數(shù)組屋匕,則還會將各個緯度的上標(biāo)和下標(biāo)信息都保存到內(nèi)存里,這主要是支持向VB這樣的可以修改數(shù)組上下標(biāo)的編程語言設(shè)計的借杰。
- 如果是引用類型过吻,則會把成員元素的類型指針保存在數(shù)組里;
- 如果是值類型蔗衡,則直接保存成員元素的內(nèi)容纤虽。
字符串對象
在托管代碼里,其在 clr\src\bcl\system\String.cs 里定義绞惦,在非托管代碼里逼纸,其在 clr\src\vm\object.h 里定義。
- 因為在.NET里字符串是不能修改的济蝉,可以將其當(dāng)作數(shù)組處理杰刽,所以.NET直接將字符串的長度保存在內(nèi)存里;
- 為了方便非托管代碼處理字符串王滤,每個字符串最后以 NULL 結(jié)尾贺嫂,當(dāng)然字符類型是WCHAR,而不是CHAR雁乡,這也就是說.NET下面字符默認是UNICODE第喳。