Object內(nèi)存核心結(jié)構(gòu)及實現(xiàn)完全剖析(MethodTable、EEClass與MethodDescChunk)
無疑,一個Object在CLR中的邏輯結(jié)構(gòu)是相當復雜的。
前段時間,寫了一篇CLR探索系列:System.Object內(nèi)存布局模型及實現(xiàn)研究,側(cè)重從System.Object這個基本類的基本內(nèi)存布局,實現(xiàn)和結(jié)構(gòu)來研究了下。這是遠遠不夠的。今天就從如何存儲一個Object中的Field,Method等信息,這些信息的邏輯組織方式和存儲的邏輯結(jié)構(gòu)。
廢話不多說,看看就知道了:
首先,給一個圖:
這個圖,顯示了一個Object的MethodTable,EEClass和MethodDescChunk之間的大致關(guān)系。至于一個整體的Object的實現(xiàn)的邏輯結(jié)構(gòu)模型圖,結(jié)構(gòu)比較復雜,不好畫,google了半天也沒找到個現(xiàn)成的,就用這個湊合了。
通常,在內(nèi)存中,對一個instance的ref的這個地址,其實是指向這個instance的MethodTable的。這個內(nèi)容,我在“System.Object這個基本類的基本內(nèi)存布局,實現(xiàn)和結(jié)構(gòu)”這篇文章里面也有說,具體是如何實現(xiàn)的可以參照那篇文章。
老規(guī)矩,截取點MethdTable里面重要的屬性,方法和結(jié)構(gòu)體,總共一個MethodTable的實現(xiàn),就大概2500多行,(太長了?這僅僅是定義部分),只能截取表示如下了:
class MethodTable
{
// Low WORD is component size for array and string types, zero otherwise
DWORD m_wFlags;
// Base size of instance of this class when allocated on the heap
DWORD m_BaseSize;
//Nmuber of Method
WORD m_wNumMethods;
//Number of Vitural Methods
WORD m_wNumVirtuals;
WORD m_wNumInterfaces;
// LoaderModule. It is equal to the ZapModule in ngened images
PTR_Module m_pLoaderModule;
PTR_MethodTable m_pParentMethodTable;
// This is the way to create a new method table. Don't try calling new directly.
// Mehtod tables are also created in array.cpp where an EEClass
// and MethodTable are created in one fell swoop. I see no rationale basis for
// such an approach.
// Even worse almost exactly the same stuff is duplicated in DynamicMethod.cpp.
// Worse still almost exactly the same stuff is duplicated in generics.cpp.
//MethodTable的New函數(shù),MethodTalbe不能直接New一個
static MethodTable * AllocateNewMT(EEClass *pClass,
DWORD dwVtableSlots,
DWORD dwGCSize,
DWORD dwNumInterfaces,
DWORD numGenericArgs,
DWORD dwNumDicts,
DWORD dwNumTypeSlots,
ClassLoader *pClassLoader,
BaseDomain *pDomain,
BOOL isIFace,
BOOL fHasGenericsStaticsInfo,
BOOL fNeedsRemotableMethodInfo,
BOOL fNeedsRemotingVtsInfo,
BOOL fHasThreadOrContextStatics
, AllocMemTracker *pamTracker
);
// Return total vtable slots : virtual, static, and instance method slots.
unsigned GetNumMethods()
{
LEAF_CONTRACT;
return m_wNumMethods;
}
void SetNumMethods(WORD wNumSlots)
{
LEAF_CONTRACT;
m_wNumMethods = wNumSlots;
}
//使用專門的數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)對Table中的可寫部分的讀寫。而這些可寫的部分,是定義在另外的一個專門的枚舉類型的變量中。
PTR_MethodTableWriteableData m_pWriteableData;
//MethodTable類中最重要的一個定義,包涵了對EEClass的引用。
PTR_EEClass m_pEEClass;
//在尋找所謂的VTable定義的時候,著實費了一番功夫,最后得到一個結(jié)論:在sscli2.0中,去掉了m_Vtable[1]的顯示定義。更多的是把Vtable當作一個Thunking Layer來在需要的時候操作。
//為此,MethodTalbe的定義中,還專門定義了一個struct(InteropMethodTableSlotData)來實現(xiàn)模擬就得Vtable結(jié)構(gòu)對COM接口的訪問。在需要使用到類似Vtable的時候直接計算某個Slot的Offset,并且使用專門的Accessor來進行訪問。截取兩個方法來說明這點:
inline PTR_SLOT GetVtable()
{
LEAF_CONTRACT;
return PTR_SLOT((PTR_HOST_TO_TADDR(this) + TADDR(GetVtableOffset())));
}
static inline DWORD GetVtableOffset()
{
LEAF_CONTRACT;
return (sizeof(MethodTable));
}
}
從下圖中也可以很明顯的看出MethodTable的結(jié)構(gòu)以及其部分字段的表示的含義:
最后給出一個MethodTable中重要的數(shù)據(jù)結(jié)構(gòu):
struct ThreadAndContextStaticsBucket
{
// Offset which points to the TLS storage. Allocated lazily - -1 means no offset allocated yet.
DWORD m_dwThreadStaticsOffset;
// Offset which points to the CLS storage. Allocated lazily - -1 means no offset allocated yet.
DWORD m_dwContextStaticsOffset;
// Size of TLS fields
WORD m_wThreadStaticsSize;
// Size of CLS fields
WORD m_wContextStaticsSize;
};
typedef DPTR(ThreadAndContextStaticsBucket) PTR_ThreadAndContextStaticsBucket;
在上面的部分屬性中,可以看到,一個Object的信息,并不是所有的都保存在MethodTalbe中的。這樣分層設(shè)計的目的,還是一個字:效率。把一個Object使用的最頻繁的部分,保持在MethodTable中,而使用的稍微不頻繁的部分,保持到EEClass中,這部分信息,從名字上面就可以看出,主要是包涵了CLR執(zhí)行引擎所需要的對一個Object的控制信息。也就是如下圖所示,各個不同的部分所包涵的內(nèi)容應用的重點和頻率都不同:
接著,給出EEClass的一些重要的數(shù)據(jù)結(jié)構(gòu):
Class EEClass
{
//得到這個Object運行的一些基本環(huán)境。
BaseDomain * GetDomain();
Assembly * GetAssembly();
Module* GetLoaderModule();
Module* GetZapModule();
//point out the Module which contains this Object and its EEClass
PTR_Module m_pModule;
mdTypeDef m_cl;
//important property!point to the MethodTable of this Object.
PTR_MethodTable m_pMethodTable;
// NOTE: Place items that are WORD sized or smaller together,
//otherwise padding will be used implicitly by the C++ compiler
WORD m_wCCtorSlot;
WORD m_wDefaultCtorSlot;
BYTE m_NormType;
//number of instance Fields
WORD m_wNumInstanceFields;
//number of static fields
WORD m_wNumStaticFields;
WORD m_wNumHandleStatics;
WORD m_wNumBoxedStatics;
//implies the number of Object ref in a instance.
//GCDesc結(jié)構(gòu),也就是一個Object實現(xiàn)的時候的頭部,Object Header可能會引用到,同時JIT也有可能引用到這個屬性。
WORD m_wNumGCPointerSeries;
DWORD m_cbModuleDynamicID;
DWORD m_cbNonGCStaticFieldBytes;
DWORD m_dwNumInstanceFieldBytes;
//這個屬性表面了對于一個Object里面的Fields,是在EEClass里面把每個Field對應的FieldDesc類連接在了一起。
FieldDesc *m_pFieldDescList;
DWORD m_dwAttrClass;
volatile DWORD m_VMFlags;
SecurityProperties m_SecProps;
//這里定義了MethodDescChunk,從最上面的圖可以看到,從一個EEClass中是連接到MethodDescThunk鏈表上面的。比較重要的一個結(jié)構(gòu)。
PTR_MethodDescChunk m_pChunks;
}
再回頭看一下,MethodTable,m_Vtable(Vtable in MethodTable),MethodDescChunks這個Chain是如何連接起來的就十分清楚了:
最后,看看MethodDescChunk這個描述每一個Method的大塊頭的結(jié)構(gòu)如何,這里,就不在分析一個MethodDescChunk里面究竟是如何實現(xiàn)的了:
MethodDescChunk的頭部是上面的藍色的部分。Prestub是中間的斜線的部分,而對每個方法的描述的部分,是上圖的白色的區(qū)域,可以看到,包涵了這個方法的IL代碼,SlotNumber,和一些標識位。每一行,是一個DWORD類型。
注意:m_op中,包涵了對這個方法的調(diào)用,是call JIT來進行實時編譯,還是jump到一個已經(jīng)編譯好了的地方,m_op就是標識這兩種不同的操作類型。而m_target里面,則是和m_op對應的內(nèi)存地址。
Ok,寫到這里,結(jié)合第一次講System.Object的那篇文章,我想,對一個Object的認識應該有個大體的比較清晰的架構(gòu)了。
最后,做個廣告^_^
歡迎志同道合的同志們加入:Share Source CLI核心技術(shù)探索團隊,這里,有一群對DotNet世界最核心技術(shù)充滿好奇和執(zhí)著探索的朋友們,期待你充滿智慧的文章和加盟。
posted on 2008-03-14 19:55 lbq1221119 閱讀(5384) 評論(13) 收藏 舉報
浙公網(wǎng)安備 33010602011771號