zoukankan      html  css  js  c++  java
  • Object内存核心结构及实现完全剖析

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

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

    废话不多说,看看就知道了:

    首先,给一个图:

     


          
      
        这个图,显示了一个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世界最核心技术充满好奇和执着探索的朋友们,期待你充满智慧的文章和加盟。

  • 相关阅读:
    代码规范
    今日头条广告投放
    网络广告计费方式CPM、CPA、CPS、CPT、CPC及比较分析
    dedecms arclist分页
    nginx配置http访问自动跳转到https
    阿里云《nginx服务器配置SSL证书》 配置参数
    JavaScript 通过身份证号获取出生日期、年龄、性别 、籍贯
    Bootstrap自适应各种设备
    css3动画大全
    golang
  • 原文地址:https://www.cnblogs.com/qianyz/p/2234705.html
Copyright © 2011-2022 走看看