您的位置:首页 > 移动开发 > Objective-C

Object内存核心结构及实现完全剖析(MethodTable、EEClass与MethodDescChunk)

2008-03-14 19:25 567 查看
无疑,一个Object在CLR中的逻辑结构是相当复杂的。
前段时间,写了一篇CLR探索系列:System.Object内存布局模型及实现研究,侧重从System.Object这个基本类的基本内存布局,实现和结构来研究了下。这是远远不够的。今天就从如何存储一个Object中的Field,Method等信息,这些信息的逻辑组织方式和存储的逻辑结构。

废话不多说,看看就知道了:
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 

首先,给一个图:

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />



      
  
    这个图,显示了一个Object的MethodTable,EEClass和MethodDescChunk之间的大致关系。至于一个整体的Object的实现的逻辑结构模型图,结构比较复杂,不好画,google了半天也没找到个现成的,就用这个凑合了。
通常,在内存中,对一个instance的ref的这个地址,其实是指向这个instance的MethodTable的。这个内容,我在“System.Object这个基本类的基本内存布局,实现和结构”这篇文章里面也有说,具体是如何实现的可以参照那篇文章。
老规矩,截取点MethdTable里面重要的属性,方法和结构体,总共一个MethodTable的实现,就大概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函数,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;
    }

       //使用专门的数据结构来实现对Table中的可写部分的读写。而这些可写的部分,是定义在另外的一个专门的枚举类型的变量中。

PTR_MethodTableWriteableData m_pWriteableData;
//MethodTable类中最重要的一个定义,包涵了对EEClass的引用。

PTR_EEClass     m_pEEClass;

      
       //在寻找所谓的VTable定义的时候,着实费了一番功夫,最后得到一个结论:在sscli2.0中,去掉了m_Vtable[1]的显示定义。更多的是把Vtable当作一个Thunking Layer来在需要的时候操作。
//为此,MethodTalbe的定义中,还专门定义了一个struct(InteropMethodTableSlotData)来实现模拟就得Vtable结构对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的结构以及其部分字段的表示的含义:



 
       最后给出一个MethodTable中重要的数据结构:

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中的。这样分层设计的目的,还是一个字:效率。把一个Object使用的最频繁的部分,保持在MethodTable中,而使用的稍微不频繁的部分,保持到EEClass中,这部分信息,从名字上面就可以看出,主要是包涵了CLR执行引擎所需要的对一个Object的控制信息。也就是如下图所示,各个不同的部分所包涵的内容应用的重点和频率都不同:
      



       接着,给出EEClass的一些重要的数据结构:

Class EEClass
{
       //得到这个Object运行的一些基本环境。

       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结构,也就是一个Object实现的时候的头部,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链表上面的。比较重要的一个结构。

    PTR_MethodDescChunk m_pChunks;
}

       再回头看一下,MethodTable,m_Vtable(Vtable in MethodTable),MethodDescChunks这个Chain是如何连接起来的就十分清楚了:
 


       最后,看看MethodDescChunk这个描述每一个Method的大块头的结构如何,这里,就不在分析一个MethodDescChunk里面究竟是如何实现的了:



      
MethodDescChunk的头部是上面的蓝色的部分。Prestub是中间的斜线的部分,而对每个方法的描述的部分,是上图的白色的区域,可以看到,包涵了这个方法的IL代码,SlotNumber,和一些标识位。每一行,是一个DWORD类型。

注意:m_op中,包涵了对这个方法的调用,是call JIT来进行实时编译,还是jump到一个已经编译好了的地方,m_op就是标识这两种不同的操作类型。而m_target里面,则是和m_op对应的内存地址。

 

Ok,写到这里,结合第一次讲System.Object的那篇文章,我想,对一个Object的认识应该有个大体的比较清晰的架构了。
 

最后,做个广告^_^
欢迎志同道合的同志们加入:Share Source CLI核心技术探索团队,这里,有一群对DotNet世界最核心技术充满好奇和执着探索的朋友们,期待你充满智慧的文章和加盟。
 

<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />2008-3-14 19:48:01
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐