Object结构初探
前言:
本来lbq同学在这里已经将Object基本分析个底朝天了,然而最近看Shared Source Internal 看的手痒,于是小生还是不禁想补充一下,顺便也当自己理清一下思路好了 J, 如果您能从本文带来一点收获,当然是高兴不过了
本文逻辑
从Object的对象布局入手,结合SSCLI代码对于Object的结构做一次整体解析
引言
Object是所有对象的基础,如 ECMA335中所说,Common Language Runtime中提出Object的概念是为了兼容所有的编程语言,强类型如Java, 动态语言如Python等等. 在Object之上派生出 类型系统等等基本架构,而Object本身的设计则考虑到反射,GC回收等特性做了一些自己的设计。相关的资料很难查及,勉强入手,还望指教
Object结构
Figure 1: Object布局, FROM Shared Source CLI 2.0 Draft
看看Object.cpp中的Object Class (部分截取)
class Object
{
protected:
MethodTable* m_pMethTab;
EEClass* GetClass()
{
WRAPPER_CONTRACT;
return( GetMethodTable()->GetClass() );
}
TypeHandle GetTypeHandle();
TypeHandle GetTrueTypeHandle();
// Sync Block & Synchronization services
// Access the ObjHeader which is at a negative offset on the object (because of
// cache lines)
ObjHeader *GetHeader()
{
LEAF_CONTRACT;
return PTR_ObjHeader(PTR_HOST_TO_TADDR(this) - sizeof(ObjHeader));
}
friend class ObjHeader;
};
-
SyncBlock
题外话: Lbq在这里讲过, 然而刚开始我去找SyncBlock.h和Object.h中却怎么也找不到SyncBlock这个变量,只存在GetSyncBlockIndex这个方法,只有对应的Index那保存这个Index的Table呢? 他的名字是g_pSyncTable, 他是一个全局表用来存储所有的SyncBlock.
SyncBlock这个名称其实不太符合实际作用,他有两种作用
- 同步锁的作用
- 作垃圾回收时的索引
他是怎么达到的呢? 查看下代码
// Access to the Sync Block Index, by masking the Value.
DWORD GetHeaderSyncBlockIndex()
{
LEAF_CONTRACT;
// pull the value out before checking it to avoid race condition
DWORD value = m_SyncBlockValue;
if ((value & (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE)) != BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX)
return 0;
return value & MASK_SYNCBLOCKINDEX;
}
我们看到,其实m_SyncBlockValue用一个DWORD大小的值来标示两种状态. 算法是在返回之前会与对应的BIGFLAGS进行一次与,或运算
我们看其中两个, 更多的定义在CLR/SRC/VM/ObjHeader.h 的注释里
// This lock is only taken when we need to modify the index value in m_SyncBlockValue.
// It should not be taken if the object already has a real syncblock index.
#define BIT_SBLK_SPIN_LOCK 0x10000000
#define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX 0x08000000
// if BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX is set,
// then if BIT_SBLK_IS_HASHCODE is also set, the rest of the dword is the hash code (bits 0 thru 25),
// otherwise the rest of the dword is the sync block index (bits 0 thru 25)
#define BIT_SBLK_IS_HASHCODE 0x04000000
当设置值时,则有
if (FastInterlockCompareExchange((LONG*)&m_SyncBlockValue, newValue, oldValue) == oldValue)
{
return;
}
// 检查是否为符合范围的值
也许你会问,锁和垃圾回收是如何实现的呢?
对于锁, lock关键字锁对应的代码为
lock(obj);
// Equals to code below
Monitor.Enter(obj);
try
{
// selection here
}
finally
{
Montior.Exit(obj);
}
-
MethodTable 和 EEClass
首先看看Object.h中对于MethodTable和EEClass的获取方式
MethodTable:
protected:
MethodTable* m_pMethTab;
public:
#define MARKED_BIT 0x1
MethodTable *GetMethodTable() const
{
LEAF_CONTRACT;
#if !defined(DACCESS_COMPILE)
// We should always use GetGCSafeMethodTable() if we're running during a GC.
// If the mark bit is set then we're running during a GC
_ASSERTE((((size_t)m_pMethTab) & MARKED_BIT) == 0);
#endif // !DACCESS_COMPILE
return PTR_MethodTable((TADDR) m_pMethTab);
}
EEClass* GetClass()
{
WRAPPER_CONTRACT;
return( GetMethodTable()->GetClass() );
}
可以看到GetClass的方法说调用的是MethodTable中的GetClass方法来获取EEClass,我们进去看看这个方法
inline EEClass* MethodTable::GetClass()
{
LEAF_CONTRACT;
// the only time a MT can have a NULL EEClass, and normal callers will never encounter this case.
// the above PREFIX_ASSUME slows down the checked build (see CHECK class in inc\check.h)
// this is a frequent call site, it needs faster asserts
_ASSERTE_IMPL(m_wFlags2 != enum_flag2_IsAsyncPin);
_ASSERTE_IMPL(m_pEEClass != NULL);
return m_pEEClass;
}
private:
PTR_Module m_pModule;
mdTypeDef m_cl;
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;
WORD m_wNumInstanceFields;
WORD m_wNumStaticFields;
WORD m_wNumHandleStatics;
WORD m_wNumBoxedStatics;
WORD m_wNumGCPointerSeries;
DWORD m_cbModuleDynamicID;
DWORD m_cbNonGCStaticFieldBytes;
DWORD m_dwNumInstanceFieldBytes;
#ifndef DACCESS_COMPILE
FieldDesc *m_pFieldDescList; // 保存字段列表
#else // DACCESS_COMPILE
FieldDesc* m_pFieldDescList_UseAccessor;
#endif // DACCESS_COMPILE
DWORD m_dwAttrClass;
volatile DWORD m_VMFlags;
SecurityProperties m_SecProps;
PTR_MethodDescChunk m_pChunks; // 保存方法表数据
关于MethodDescChunk, MethodTable, 当然还有那VTable, lbq这篇文章讲的很精彩 参考里也有链接
参考
Custom Object Layout for Garbage-Collected Languages
Explanatory notes on Rotor's Garbage Collector
CLR探索系列:System.Object内存布局模型及实现研究
希望本文能给你有所帮助