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

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

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

Object在内存中的结构

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

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

  • 指向头部的指针

  • 指向类型信息的指针

  • 字段内容

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

frameborder="0" scrolling="no" style="border-width: initial; border-style: none; width: 550px; height: 589px;">

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的生成我将在后面的篇幅中介绍

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 _WIN64DWORD    m_alignpad;#endif // _WIN64Volatile<DWORD> m_SyncBlockValue;      // the Index and the Bits}

m_alignpad是用于对齐的(让m_SyncBlockValue在后面4位),值应该为0
m_SyncBlockValue的前6位是标记,后面26位是对应的SyncBlockSyncBlockCache中的索引
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 heapDWORD 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 _DEBUGLPCUTF8 debug_m_szClassName;#endif //_DEBUG// Parent PTR_MethodTable if enum_flag_HasIndirectParent is not set. Pointer to indirection cell// if enum_flag_enum_flag_HasIndirectParent is set. The indirection is offset by offsetof(MethodTable, m_pParentMethodTable).// It allows casting helpers to go through parent chain natually. Casting helper do not need need the explicit check// for enum_flag_HasIndirectParentMethodTable.TADDR m_pParentMethodTable;PTR_Module m_pLoaderModule;    // LoaderModule. It is equal to the ZapModule in ngened imagesPTR_MethodTableWriteableData m_pWriteableData;    union {EEClass *   m_pEEClass;TADDR       m_pCanonMT;};    // m_pPerInstInfo and m_pInterfaceMap have to be at fixed offsets because of performance sensitive // JITed code and JIT helpers. However, they are frequently not present. The space is used by other// multipurpose slots on first come first served basis if the fixed ones are not present. The other // multipurpose are DispatchMapSlot, NonVirtualSlots, ModuleOverride (see enum_flag_MultipurposeSlotsMask).// The multipurpose slots that do not fit are stored after vtable slots.union{PTR_Dictionary *    m_pPerInstInfo;TADDR               m_ElementTypeHnd;TADDR               m_pMultipurposeSlot1;};    union{InterfaceInfo_t *   m_pInterfaceMap;TADDR               m_pMultipurposeSlot2;};    // 接下来还有一堆OPTIONAL_MEMBERS,这里省去介绍}

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

  • 类型的标记,例如StaticsMask_DynamicStaticsMask_Generics等 (m_dwFlags)

    • 如果类型是字符串或数组还会保存每个元素的大小(ComponentSize),例如string是2 int[100]是4

  • 类型需要分配的内存大小 (m_BaseSize)

  • 类型信息,例如有哪些成员和是否接口等等 (m_pCanonMT)

可以看出这个类型就是用于保存类型信息的,反射和动态Cast都需要依赖它

实际查看内存中的Object

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

这里我定义了MyClassMyStruct类型,先看Console.WriteLine(myClass)
这里把第一个参数设置到rcx并且调用Console.WriteLine函数,为什么是rcx请看查看参考链接中对fastcall的介绍
rbp + 0x50 = 0x1fc8fde110

跳到内存中以后可以看到选中的这8byte是指向对象的指针,让我们继续跳到0x1fcad88390

这里我们可以看到MyClass实例的真面目了,选中的8byte是指向MethodTable的指针
后面分别是指向StringMember的指针和IntMember的内容
在这里指向ObjHeader的指针是一个空指针,这是正常的,微软在代码中有注释This is often zero

这里是StringMember指向的内容,分别是指向MethodTable的指针,字符串长度和字符串内容

这里是MyClassMethodTablem_BaseSize是32
有兴趣的可以去和MethodTable的成员一一对照,这里我就不跟下去了
让我们再看下struct是怎么处理的

可以看到只是简单的把值复制到了堆栈空间中(rbp是当前frame的堆栈基础地址)
让我们再来看下Console.WriteLine对于struct是怎么处理的,这里的处理相当有趣

因为需要装箱,首先会要来一个箱子,箱子放在了rbp+30h

MyStruct中的值复制到了箱子中,rax+8的8是把值复制到MethodTable之后

复制后,接下来把这个箱子传给Console.WriteLine就和MyClass一样了

另外再附一张实际查看ComponentSize的图

彩蛋

看完了.Net中对Object的定义,让我们再看下Python中队Object的定义
源代码: https://github.com/python/cpython/blob/master/Include/object.h

#define PyObject_HEAD PyObject ob_base; // 每个子类都需要把这个放在最开头typedef struct _object {#ifdef Py_TRACE_REFSstruct _object *_ob_next; // Heap中的前一个对象struct _object *_ob_prev; // Heap中的后一个对象#endifPy_ssize_t ob_refcnt; // 引用计数struct _typeobject *ob_type; // 指向类型信息} PyObject;

定义不一样,但是作用还是类似的

参考

http://stackoverflow.com/questions/20033353/clr-implementation-of-virtual-method-calls-via-pointer-to-base-class
http://stackoverflow.com/questions/9808982/clr-implementation-of-virtual-method-calls-to-interface-members
http://stackoverflow.com/questions/1589669/overhead-of-a-net-array
https://en.wikipedia.org/wiki/X86_calling_conventions
https://github.com/dotnet/coreclr/blob/master/src/vm/object.inl
https://github.com/dotnet/coreclr/blob/master/src/vm/object.h
https://github.com/dotnet/coreclr/blob/master/src/vm/object.cpp
https://github.com/dotnet/coreclr/blob/master/src/vm/syncblk.h
https://github.com/dotnet/coreclr/blob/master/src/vm/syncblk.cpp
https://github.com/dotnet/coreclr/blob/master/src/vm/methodtable.inl
https://github.com/dotnet/coreclr/blob/master/src/vm/methodtable.h
https://github.com/dotnet/coreclr/blob/master/src/vm/methodtable.cpp
https://github.com/dotnet/coreclr/blob/master/src/vm/class.h
https://github.com/dotnet/coreclr/blob/master/src/inc/daccess.h
https://github.com/dotnet/coreclr/blob/master/src/debug/daccess/dacfn.cpp

写在最后

因为是刚开始阅读coreclr的代码,如果有误请在留言中指出
接下来有时间我将会着重阅读和介绍这些内容

  • Object的生成和销毁

  • Object继承的原理(MethodTable)

  • Object同步的原理(ObjHeader, SyncBlock)

  • GC的工作方式

  • DACCESS

原文地址:http://www.cnblogs.com/zkweb/p/6244934.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/327256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布式的CAP理论

转载自 分布式的CAP理论 CAP理论概述 2000年7月&#xff0c;加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后&#xff0c;麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。之后&#xff0c;CAP理论正式成为分布式计算领域的公认定理。 一…

使用navicat 复制 数据库 使用mysql5.7 utf8mb4 utf8mb4_general_ci 爬虫的时候就支持表情包了

使用mysql5.7 utf8mb4 utf8mb4_general_ci 爬虫的时候就支持表情包了 mysql8.0差别有点大 将python_spider 完整的复制到python_spider_copy上面 1.右键选中python_spider 选择数据传输 默认传输所有表&#xff0c;你可以进行选择 工具里面的数据…

设计爬虫Hawk背后的故事

本文写于圣诞节北京下午慵懒的午后。本文偏技术向&#xff0c;不过应该大部分人能看懂。 五年之痒 2016年&#xff0c;能记入个人年终总结的事情没几件&#xff0c;其中一个便是开源了Hawk。我花不少时间优化和推广它&#xff0c;得到的评价还算比较正面&#xff0c;因为负面评…

wrapper 并集如何使用

https://blog.csdn.net/u011229848/article/details/81902398 MybatisPlus QueryWrapper and or 连用 cnsu-cmh 2018-08-21 10:08:03 96287 收藏 26 分类专栏&#xff1a; MybatisPlus 文章标签&#xff1a; MybatisPlus and or 连用 QueryWrapper and 版权 QueryWrapper&l…

用quot;hosting.jsonquot;配置ASP.NET Core站点的Hosting环境

通常我们在 Prgram.cs 中使用硬编码的方式配置 ASP.NET Core 站点的 Hosting 环境&#xff0c;最常用的就是 .UseUrls() 。 public class Program { public static void Main(string[] args){ var host new WebHostBuilder() .UseUrls("http://*:5000"…

程序员常用网站收藏[定期更新]——csdn博客

网站收藏【定期更新】 资源类1.开源中国社区3.CodePlayer专注于编程技术分享4.源码之家5.阿里云6.免费logo在线制作, 标志设计, 公司logo设计7.搜素材 - 站酷 (ZCOOL)8.站长素材97.建站资源_伊思娜资源网-2014国内最全|免费空间|域名|网赚|免费cmcc|网络电话|网站优化|服装搭配…

No primary or default constructor found for interface java.util.List

https://blog.csdn.net/qq_39723363/article/details/84379685 springboot No primary or default constructor found for interface java.util.List 夏天吹雪 2018-11-23 11:31:12 67570 收藏 6 分类专栏&#xff1a; 工作随记 版权 RequestParam(value "content&…

论:开发者信仰之“天下IT是一家“(Java .NET篇)

比尔盖茨公认的IT界领军人物&#xff0c;打造了辉煌一时的PC时代。 2008年&#xff0c;史蒂夫鲍尔默接替了盖茨的工作&#xff0c;成为微软公司的总裁。 2013年他与微软做了最后的道别。 2013年以后&#xff0c;我才真正看到了微软的变化。尤其是它的“云优先&#xff0c;移动优…

配置高性能ElasticSearch集群的9个小贴士

Loggly服务底层的很多核心功能都使用了ElasticSearch作为搜索引擎。就像Jon Gifford&#xff08;译者注&#xff1a;Loggly博客作者之一&#xff09;在他近期关于“ElasticSearch vs Solr”的文章中所述&#xff0c;日志管理在搜索技术方面产生一些粗暴的需求&#xff0c;坚持下…

MyBatis-Plus Wrapper条件构造器查询大全

https://blog.csdn.net/weixin_38111957/article/details/91447509 MyBatis-Plus Wrapper条件构造器查询大全 IT贱男 2019-06-11 16:50:44 36133 收藏 66 分类专栏&#xff1a; Mybatis-Plus 版权 码农必备linux技能 本专栏主要从系统安装、入门命令讲解&#xff0c;会不…

.NET异常设计原则

异常是使用.NET时必然会遇到的问题&#xff0c;但是&#xff0c;有太多的开发人员没有从API设计的角度考虑这个问题。在大部分工作中&#xff0c;他们自始至终都知道需要捕获什么异常以及哪些异常需要写入全局日志。如果你设计了可以让你正确使用异常的API&#xff0c;则可以显…

java 百度经纬度 转换为 高德经纬度 谷歌

https://lbs.amap.com/api/webservice/guide/api/convert/#convert https://restapi.amap.com/v3/assistant/coordinate/convert?locations116.481499,39.990475&coordsysgps&outputxml&key<用户的key> https://console.amap.com/dev/key/app https://conso…

方法的重写VS重载

方法的重写&#xff08;override&#xff09; 方法的重写 子类从父类中继承方法&#xff0c;有时&#xff0c;子类需要修改父类中定义的方法的实现&#xff0c;这称做方法的重写(method overriding)。“重写”的概念与“重载”相似&#xff0c;它们均是Java“多态”的技术之一&…

权衡微服务

很多开发团队已经认识到 微服务架构比单体架构更优越。但是也有其他团队感觉到这是一种消弱生产力的负担。就像任何软件架构&#xff0c;微服务架构同样有利弊。为了能做出一个明智的选择&#xff0c;你必须了解这些应用并将它们运用到你特定的环境中。 微服务的优势 具有边界的…

使用IDM下载,不适用默认浏览器下载

google浏览器是不能够安装的除非有v*p*n&#xff08;&#xff09; Firefox浏览器可以安装js脚本 推荐使用 1 google 扩展 IDMan628 扩展程序里面加 IDMGCExt.crx 2 扩展Tampermonkey 加Tampermonkey.crx 3 点击Tampermonkey 的仪表盘 右上角加按钮 百度网盘直接下载助手…