HTML5技术

CoreCLR源码探索(一) Object是什么 - q303248153

字号+ 作者:H5之家 来源:H5之家 2017-01-06 11:04 我要评论( )

.Net程序员们每天都在和Object在打交道 如果你问一个.Net程序员什么是Object,他可能会信誓旦旦的告诉你"Object还不简单吗,就是所有类型的基类" 这个答案是对的,但是不足以说明Object真正是什么 在这篇文章我们将会通过阅读CoreCLR的源代码了解Object在内

.Net程序员们每天都在和Object在打交道
如果你问一个.Net程序员什么是Object,他可能会信誓旦旦的告诉你"Object还不简单吗,就是所有类型的基类"
这个答案是对的,但是不足以说明Object真正是什么

在这篇文章我们将会通过阅读CoreCLR的源代码了解Object在内存中的结构和实际到内存中瞧瞧Object

勘误

通过更多的测试我发现了以下的错误,在此做出纠正,请之前的读者见谅

  • Object前面的不是指向ObjHeader的指针而是ObjHeader自身
  • Object在内存中的结构

    为了便于理解后面的内容,我先用一张图说明Object在内存中的结构

    .Net中的Object包含了这三个部分

    微软有一张更全的图(说明的是.Net Framework的结构,但是基本和.Net Core一样)

    Object的源代码解析

    Object的定义(摘要)
    源代码: https://github.com/dotnet/coreclr/blob/master/src/vm/object.h

    class Object { PTR_MethodTable m_pMethTab; }

    PTR_MethodTable的定义,DPTR是一个指针的包装类,你可以先理解为MethodTable*的等价
    源代码: https://github.com/dotnet/coreclr/blob/master/src/vm/common.h

    typedef DPTR(class MethodTable) PTR_MethodTable;

    在Object的定义中我们只看到了一个成员,这个成员就是指向类型信息的指针,那其他两个部分呢?

    这是获取指向头部的指针的函数,我们可以看到对象头部刚好放在了Object的前面

    PTR_ObjHeader GetHeader() { LIMITED_METHOD_DAC_CONTRACT; return dac_cast<PTR_ObjHeader>(this) - 1; }

    这是获取字段内容的函数,我们可以看到字段内容刚好放在了Object的后面

    PTR_BYTE GetData(void) { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return dac_cast<PTR_BYTE>(this) + sizeof(Object); }

    我们可以看到Object中虽然只定义了指向类型信息的指针,但运行时候前面会带对象头部,并且后面会带字段内容
    Object结构比较特殊,所以这个对象的生成也需要特殊的处理,关于Object的生成我将在后面的篇幅中介绍

    Object中定义的m_pMethTab还保存了额外的信息,因为这是一个指针值,所以总会以4或者8对齐,这样最后两个bit会总是为0
    .Net利用了这两个闲置的bit,分别用于保存GC Pinned和GC Marking,关于这里我也将在后面的篇幅中介绍

    ObjHeader的源代码解析

    ObjHeader的定义(摘要)
    源代码: https://github.com/dotnet/coreclr/blob/master/src/vm/syncblk.h

    class ObjHeader { // !!! Notice: m_SyncBlockValue *MUST* be the last field in ObjHeader. #ifdef _WIN64 DWORD m_alignpad; #endif // _WIN64 Volatile<DWORD> m_SyncBlockValue; // the Index and the Bits }

    m_alignpad是用于对齐的(让m_SyncBlockValue在后面4位),值应该为0
    m_SyncBlockValue的前6位是标记,后面26位是对应的SyncBlock在SyncBlockCache中的索引
    SyncBlock的作用简单的来说就是用于线程同步的,例如下面的代码会用到SyncBlock

    var obj = new object(); lock (obj) { }

    ObjHeader只包含了SyncBlock,所以你可以看到有的讲解Object结构的文章中会用SyncBlock代替ObjHeader
    关于SyncBlock更具体的讲解还可以查看这篇文章

    MethodTable的源代码解析

    MethodTable的定义(摘要)
    源代码: https://github.com/dotnet/coreclr/blob/master/src/vm/methodtable.h

    class MethodTable { // Low WORD is component size for array and string types (HasComponentSize() returns true). // Used for flags otherwise. DWORD m_dwFlags; // Base size of instance of this class when allocated on the heap DWORD m_BaseSize; WORD m_wFlags2; // Class token if it fits into 16-bits. If this is (WORD)-1, the class token is stored in the TokenOverflow optional member. WORD m_wToken; // <NICE> In the normal cases we shouldn't need a full word for each of these </NICE> WORD m_wNumVirtuals; WORD m_wNumInterfaces; #ifdef _DEBUG LPCUTF8 debug_m_szClassName; TADDR m_pParentMethodTable; PTR_Module m_pLoaderModule; // LoaderModule. It is equal to the ZapModule in ngened images PTR_MethodTableWriteableData m_pWriteableData; union { EEClass * m_pEEClass; TADDR m_pCanonMT; }; { PTR_Dictionary * m_pPerInstInfo; TADDR m_ElementTypeHnd; TADDR m_pMultipurposeSlot1; }; union { InterfaceInfo_t * m_pInterfaceMap; TADDR m_pMultipurposeSlot2; }; // 接下来还有一堆OPTIONAL_MEMBERS,这里省去介绍 }

    这里的字段非常多,我将会在后面的篇幅一一讲解,这里先说明MethodTable中大概有什么信息

  • 类型的标记,例如StaticsMask_Dynamic和StaticsMask_Generics等 (m_dwFlags)
  • 如果类型是字符串或数组还会保存每个元素的大小(ComponentSize),例如string是2 int[100]是4
  • 类型需要分配的内存大小 (m_BaseSize)
  • 类型信息,例如有哪些成员和是否接口等等 (m_pCanonMT)
  • 可以看出这个类型就是用于保存类型信息的,反射和动态Cast都需要依赖它

    实际查看内存中的Object

    对Object的初步分析完了,可分析对了吗?让我们来实际检查一下内存中Object是什么样子的
    VisualStudio有反编译和查看内存的功能,如下图

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • 升讯威ADO.NET增强组件(源码):送给喜欢原生ADO.NET的你 - sheng.chao

      升讯威ADO.NET增强组件(源码):送给喜欢原生ADO.NET的你 - sheng.c

      2016-12-20 11:01

    • html5 Sortable.js 拖拽排序源码分析 - qq281113270

      html5 Sortable.js 拖拽排序源码分析 - qq281113270

      2016-11-04 15:00

    • MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则) - 懒得安分

      MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

      2016-11-03 18:00

    • MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码) - 懒得安分

      MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码) - 懒得安

      2016-10-31 15:00

    网友点评