.NET运行时之书(Book of the Runtime,简称BotR)是一系列描述.NET运行时的文档,2007年左右在微软内部创建,最初目的是为了帮助其新员工快速上手.NET运行时;随着.NET开源,BotR也被公开了出来,如果想深入理解CLR,这系列文章不可错过。
BotR系列目录:
[1] CLR类型加载器设计(Type Loader Design)
[2] CLR类型系统概述(Type System Overview)
原文:https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/type-system.md
作者: David Wrighton - 2010
翻译:几秋 (https://www.cnblogs.com/netry/)
CLR类型系统是我们在ECMA规范+扩展中描述的类型系统的表示形式。
该类型系统是由一系列数据结构(其中一些在BotR的其它章节有描述)和操作这些数据结构的算法组合而成,它不是通过反射暴露出来的类型系统,尽管反射确实依赖于这个系统。
由类型系统维护的主要数据结构是:
由类型系统包含的主要算法是:
还有很多辅助数据结构和算法为CLR的其余部分提供各种信息,但它们对于整个系统的理解并不那么重要。
类型系统的数据结构通常被各种算法所使用。本文档不会涉及类型系统算法(因为BotR的其它章节有涉及这些),但是它会试图描述各种主要数据结构。
类型系统大体上是给CLR中的很多部分提供服务,多数核心组件都对类型系统的行为有某种形式的依赖性。下图描述了影响类型系统的通用数据流,它不是很全面,但是指出了只要的信息流。

类型系统的主要依赖关系如下:
类型系统有3个主要组件依赖于它:
核心类型系统数据结构是表示实际加载类型的数据结构(例如,TypeHandle, MethodTable, MethodDesc, TypeDesc, EEClass)和允许在加载类型后找到它们的数据结构(例如,ClassLoader, Assembly, Module, RIDMaps)。
加载类型的数据结构和算法在BotR的Type Loader 和 MethodDesc章节中有讨论。
将这些数据结构绑定在一起是一组功能, 允许JIT/Reflection/TypeLoader/stackwalker去查找现存类型和方法,一般的想法是,这些搜索应该很容易地由ECMA CLI规范中指定的元数据令牌/签名(metadata tokens/signatures)驱动。
最后,当合适的类型系统数据结构被找到,我们有算法从类型中收集信息,有and/or比较两个类型。可以在 Virtual Stub Dispatch找到这种算法的一个特别复杂的例子。
类型转换算法(casting algorithm)是类型系统中的典型算法,在托管代码的执行过程中大量使用这种算法。
这个算法至少有4个独立的入口(entry point),选择每个入口都是为了提供不同的快速路径,希望能够实现最佳的性能。
除了最后一个之外,每个实现都进行了优化,以便在不完全通用的情况下提高性能。例如,“一个类型能否被转换成一个父类?”就是 “一个对象能否被转换成一个非类型等价的非数组类型”的变体,它通过单循环遍历一个单链表实现。这只能搜索可能的转换操作的一个子集,但可以通过检查试图强制转换的类型来确定是否是合适的集合,这个算法在jit helper JIT_ChkCastClass_Portable中实现。
假设:
在类型系统中很多算法遵循这种常见模式。类型系统通常用于查找类型,这可以通过任意数量的输入触发,如JIT、反射、序列化、远程调用等等。在这些情况下,对类型系统的基本输入是:
这个算法必须首先解码标识符。对于搜索一个类型的场景,令牌可能是typedef令牌、typeref令牌、typespec令牌,或者是一个字符串。这些不同种类的标识符都会将导致不同形式的查询。
从这个设计中可以明显看出一些搜索算法在类型系统中的共同特点:
除了这个总体设计,在此基础上,还有一些额外的需求:
此搜索算法是 JIT期间使用的典型程序。它具有许多共同的特征:
这使我们能够满足性能要求,以及使用基于IL的JIT所必需的特征。
垃圾回收器要有关类型实例分配在GC堆上的信息,这是通过一个指向类型系统数据结构(MethodTable)的指针来完成,该MethodTable位于每一个托管对象的开头。附着到这个MethodTable之上的,是一个描述类型实例GC布局的数据结构。该布局有两种形式(一般类型和对象数组是一种,值类型数组是另一种)。
堆栈遍历器/GC堆栈遍历器在两种情况下要求类型系统输入:
由于各种原因,包括希望延迟加载类型,以及避免生成多个版本的代码(仅通过相关的gc信息不同),CLR当前需要遍历堆栈上的方法签名,这种需求很少得到满足,因为它要求在特定的时刻执行堆栈遍历,但是为了满足我们的现实目标,当遍历堆栈的时候,必须能够遍历签名。
堆栈遍历器以大约 3 种模式执行:
在GC和分析工具遍历堆栈的情况,由于线程被挂起,分配内存或占用大多数锁是不安全的。这导致我们开发了一条通过类型系统的路径,可以依赖它来遵循上述要求。型系统实现此目标所需的规则是:
这是通过一系列广泛而复杂的强制措施来实施的,包括类型加载器、NGEN镜像生成过程和JIT。
类型系统数据结构是保存到NGEN镜像中的核心部分,不幸的是,这些数据结构逻辑内有指向其它NGEN镜像的指针。为了处理这种情况,类型系统数据结构实现了一个称为恢复(restoration)的概念。
在恢复期,当第一次需要类型系统数据结构时,该数据结构用正确的指针固定, 这与类型加载级别有关,请看前篇CLR类型加载器设计
还存在一个预恢复(pre-restored)数据结构的概念,这意味着数据结构在NGEN镜像加载时足够正确(在intra-module指针和预先加载类型修正之后),数据结构可以按原样使用。此优化要求将NGEN镜像“硬绑定”("hard bound")到其依赖程序集,详情请查看NGEN相关文档。
类型系统是实现域中性加载的核心部分,它通过在AppDomain创建时启用LoaderOptimization选项暴露给用户。Mscorlib始终作为域中性加载,此功能的核心要求是类型系统数据结构不能要求指向特定域状态(domain specific state)的指针,这主要表现在围绕静态字段和类构造函数的需求中。特别是,由于这个原因,一个类的构造函数是否已经运行不是核心MethodTable数据结构的一部分。并且有一种机制来存储附加到DomainFile数据结构而不是MethodTable数据结构。
类型系统的主要部分位于:
主要入口函数是BuildMethodTable、 LoadTypeHandleThrowing、CanCastTo*、 GetMethodDescFromMemberDefOrRefOrSpecThrowing、 GetFieldDescFromMemberRefThrowing、 CompareSigs和 VirtualCallStubManager::ResolveWorkerStatic.
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。 准备工作: 1、U盘一个(尽量使用8G以上的U盘)。 2、一台正常联网可使用的电脑。 3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。 4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。 U盘启动盘制作步骤: 注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实
在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题: