原 Type System Overvie

Type System Overview

Author: David Wrighton (@davidwrighton) - 2010 简介

CLR类型系统是我们在ecma规范及其扩展的类型系统的实现。

总览

类型系统有一系列数据结构和一组创建操作这些数据结构的算法组成(这部分在其他章节有描述)。类型系统并非通过反射暴露出来的类型系统,尽管反射暴露的系统依赖于CLR类型系统。

类型系统主要维护的数据结构是:

  • MethodTable
  • EEClass
  • MethodDesc
  • FieldDesc
  • TypeDesc
  • ClassLoader

类型系统主要包含的算法是:

  • Type Loader: 用于载入类型并创建类型系统中的多数数据解构
  • CanCastTo and similar: 类型比较
  • LoadTypeHandle: 类型查找
  • Signature parsing: 用于比较获取方法或字段的类型
  • GetMethod/FieldDesc: 用于查找读取类型字段
  • Virtual Stub Dispatch: 用来查找并调用虚方法的桩

还有很多辅助数据解构和算法用于向CLR的其他部分提供信息。

组成架构

类型系统的数据结构通常被各种不同的算法使用,本文档并没有描述这些算法(引文他们应该独立成章节),而是描述了下面提到的数据类型。

依赖

类型系统通常为CLR的其他部分提供服务,大部分核心组件对类型系统都有不同程度的依赖。下面这张图描述了主要依赖关系。

###组件依赖

类型系统主要依赖

  • 元数据
  • metadata system提供收集元素据信息的接口
  • security system需要告诉类型系统特定类型系统结构能否访问
  • AppDomain 提供了处理类型数据结构分配的LoaderAllocator

依赖于类型系统的组件

主要有3个依赖于类型系统的组件

  • Jit interface jit helper主要依赖于类型,方法,字段搜索功能,如果找到了类型系统对象,就从数据结构裁剪出jit需要的信息
  • Reflection 使用类型系统来提供一种简单方式来访问ECMA标准功能,这些功能碰巧在CLR类型系统数据结构中抓取到。
  • General managed code execution 需要类型系统进行类型比较和调度虚拟方法

类型系统设计

类型系统数据的核心结构是表示和查找(例 ClassLoader, Assembly, Module, RIDMaps)实际加载的类型(例 TypeHandle, MethodTable, MethodDesc, TypeDesc, EEClass)的数据结构

加载类型的数据结构和算法在Type LoaderMethodDesc章节中详述。

这些数据结构提供了一系列的功能用于让JIT/Reflection/TypeLoader/stackwalker来查找现存类型和方法。这些搜索应该被ECMA CLI标准中的元数据tokens/signatures驱动。

最终,当找到了一个恰当的类型系统数据结构式,再使用算法来获取类型信息,and/or比较两个类型,这个算法的一个特定的复杂例子在 Virtual Stub Dispatch章节中。

设计目标和非目标

目标

  • 在运行时执行代码来获取信息是是非常快的(非反射)。
  • 在便疑似生成代码简单
  • 垃圾回收和栈追踪能够无锁且不占用内存获取必要信息
  • 每次只加载最少的类型.
  • 每次加载时只加载最少的给定类型.
  • 系统数据必须存储再NGEN镜像中 .

非目标

  • 所有元数据信息直接反映在CLR数据结构中
  • 所有的反射接口很快

托管代码运行时使用的算法设计

类型系统中强制转换算法是个经典算法,在托管代码中经常使用。

该算法至少有4个独立的入口点,每个入口都提供了一个不同的高效使用方法。

  • 一个对象能否转换成特定的非等效数组
  • 对象能否转换成一个非泛型接口
  • 能否转换成数组
  • 能否转换成任意托管类型

除了最后一个其他的实现都进行了优化以获得更好的性能,然而这是以牺牲代码一致性为代价的。

例如,类型转换成父类型是类型能够转换成非等效数组类型的变体。代码实现是在一个循环中遍历一个链表。只能搜索到可能转换操作的子集,但是可以通过尝试进行强制转换来确定这个子集是否是正确的子集,这个算法实现在jit helper中JIT_ChkCastClass_Portable。

设想

  • 通常特殊算法的目的是提升性能
  • 额外版本的算法不能产生大的维护问题

类型系统中典型搜索算法的设计

在类型系统中存在很多遵循统一模式的算法

类型系统通常用于查找类型,查找可能会被多个输入行为触发,例如JIT,反射,序列化,远程等等。

这些情况类型系统的输入是:

  • 搜索开始的上下文(一个模块或者程序集指针)
  • 在类型上下文中查找标识,标识可能是一个token或者字符串(如果实在一个程序集上下文中)

算法首先解码标识

在查找类型的场景中,token可能是类型定义,类型引用,类型描述或者是字符串,每个不同的标识符产生了不同的查找形式。

  • typedef token 在模型的RidMap上查找,这是个简单的数组指针
  • typeref token 在当前引用的程序集上面查找,类型查找算法重新在程序集上查找,并从类型引用表上收集字符串信息
  • typespec token 必须分析签名来查找引用,分析引用来加载类型的必要信息,这会触发更多的类型查找
  • name 用于程序集间绑定,在TypeDef/ExportedTypes表上面查找匹配项目,ps:这个搜索算法通过元数据模块上的哈希表优化的。

在这种设计下,类型系统中的搜索算法之间的相同特性显而易见。

  • 搜索算法与输入于元数据紧密相关,尤其是元数据符号和字符串名称传递,这些搜索与模块紧密相关,并且直接映射到dll和exe文件中
  • 缓存信息提升性能,ridmap和哈希表用于查找优化
  • 算法通常有三到四中基于不同输入的路径

除了这些基础算法设计,还有基于此的额外设计要求

  • ASSUMPTION: gc停止是,查找已经加载的类型是安全的
  • INVARIANT: 已经加载的类型以后也总能查找到
  • ISSUE: 搜索通常依赖于元数据读取,在某些情况下可能产生性能问题

搜索算法的典型使用场景是在jiting中,他又很多共同特性

  • 使用了元数据。
  • 很多地方要查找数据
  • 数据结构中需要赋值数据的很少
  • 通常不会深入递归,没有循环

这是的我们能够满足性能要求,以及基于jit的il的必要特性

垃圾回收机对类型系统的要求

垃圾回收集需要已经在GC堆中分类的类型实例的信息,这是通过在托管对象的头部加入一个类型数据结构的指针,关联到MethodTable,这个数据结构描述了类型实例在gc上的布局情况,布局有两种,一种是普通类型和对象数组,另外一种是值类型数组。

  • **ASSUMPTION:**类型系统数据结构的生命周期比其描述的托管对象的生命周期长
  • REQUIREMENT: 垃圾回收机能够在运行时挂起的状态下进行栈遍历

Stackwalker对类型系统的需求

  • 寻找栈上值类型的大小
  • 寻找栈上的gc根来获取内部值类型

由于多种原因包括延迟加载,避免生成多版本代码(仅仅gc信息不同),clr需要调用栈上的所有方法的签名,这种需求很少使用,因为这需要在非常特殊的情况下进行堆栈遍历,但为了可靠性,签名遍历必须能够在堆栈遍历的时候执行。

堆栈遍历工作在三种模态下

  • 线程安全和程序异常
  • gc,ee挂起所有线程
  • 线程分析,挂起线程

gc线程遍历和线程分析情况下,因为线程被挂起,分配内存或者是加锁会变得不安全

这导致我们通过安全的类型系统来覆盖上述要求

这些规则要求类型系统能够达到如下目标:

  • 如果调用方法,方法的所有的值类型参数会被载入进程的appdomain中
  • 程序之间通过签名引用,这也导致了在签名遍历之前必须处理好类型

This is enforced via an extensive and complicated set of enforcements within the type loader, NGEN image generation process, and JIT.

  • ISSUE: Stackwalker对类型系统的需求十分脆弱(易改变)
  • ISSUE: 在类型系统中进行堆栈遍历需要在类型系统的每个函数添加会在类型加载过程中违反约定的行为。
  • ISSUE: 执行的签名步骤是通过签名代码完成的。这些代码用于在遍历签名时执行代码
  • ISSUE: Stackwalker不仅需要类型系统还需要程序集加载器,加载器已经满足了很多类型系统的需求

Type System and NGEN

类型系统数据结构是NGEN镜像中存储的核心部分,然而,这些数据结构逻辑上存在一些引用其他NGEN镜像的指针,为了处理这种情况,类型系统数据结构实现了一个恢复机制。

在这个恢复机制中,在类型系统数据结构加载的过程中,数据结构修复指针引用,详情了解Type Loader

还存在预存储数据结构的概念。ngen镜像在加载后,数据结构是完全正确的,这个优化要求ngen镜像和它依赖的程序集之间存在硬边界,查看NGEN文档可以获得更细节的描述。

类型系统和程序集加载

类型系统是应用程序域加载的核心部分。这是通过在程序集创建是使用LoaderOptimization选项暴露的。每个程序域都会加载Mscorlib。此特性的核心要求是类型系统数据结构不能包含程序域特定状态的。这主要体现在静态字段和类构造函数中。因此,判断类构造函数是否运行不是MethodTable核心数据结构的一部分,还有一种结构用于存储和DomainFile数据结构相关的静态数据而不是放在MethodTable数据结构中。

静态构造

类型系统的主要部分:

  • Class.cpp/inl/h – EEClass functions, and BuildMethodTable
  • MethodTable.cpp/inl/h – Functions for manipulating methodtables.
  • TypeDesc.cpp/inl/h – Functions for examining TypeDesc
  • MetaSig.cpp SigParser – Signature code
  • FieldDesc /MethodDesc – Functions for examining these data structures
  • Generics – Generics specific logic.
  • Array – Code for handling the special cases required for array processing
  • VirtualStubDispatch.cpp/h/inl – Code for virtual stub dispatch
  • VirtualCallStubCpu.hpp – Processor specific code for virtual stub dispatch.

主要的入口点是 BuildMethodTable, LoadTypeHandleThrowing, CanCastTo*, GetMethodDescFromMemberDefOrRefOrSpecThrowing, GetFieldDescFromMemberRefThrowing, CompareSigs, and VirtualCallStubManager::ResolveWorkerStatic.

相关阅读

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏从零开始学 Web 前端

C/C++练习题(三)

分析:在我们看来,虽然使用字符数组和字符指针差不多,printf都可以打印出字符串出来,但是编译器对他们的处理完全不同。 对于字符指针,编译器看到后,...

8714
来自专栏PHP在线

PHP底层的运行机制与原理

PHP说简单,但是要精通也不是一件简单的事。我们除了会使用之外,还得知道它底层的工作原理。 PHP是一种适用于web开发的动态语言。具体点说,就是一个用C语言实...

1.2K6
来自专栏Python绿色通道

Python入门三部曲(三)

在函数greet_user()中,变量username是一个形参—-函数完成其工作所需要的一项信息.在代码greet_user(‘kobe’)中,值’kobe’...

1463
来自专栏微信终端开发团队的专栏

C# 内存管理机制及 WP 内存泄漏定位方法

C#内存管理机制及WP内存泄漏定位方法 一、C#的内存管理机制 1. 托管资源与非托管资源 什么是托管资源?托管资源通俗的理解就是,把资源交给.net去管理,这...

4257
来自专栏MyBlog

Effective Java 读书笔记(7)避免finalizer

对于Finalizers他们的使用可能会造成错误的产生,糟糕的性能以及移植性的问题,当然Finalizers有着一些有用的优点,我们会在后续介绍这些,但是作为首...

1102
来自专栏喔家ArchiSelf

全栈必备 Java基础

那一年,从北邮毕业,同一年,在大洋的彼岸诞生了一门对软件业将产生重大影响的编程语言,它就是——Java。1998年的时候,开始学习Java1.2,并在Java ...

1184
来自专栏猿人谷

腾讯2013年实习生笔试题目(附答案)

下面是我在参加2013年腾讯实习生招聘的笔试题目,当然啦,我个人不可能是完全的记住所有题目,部分是摘自网络的。同时,下面也有一些题目我不会的,希望大家一起商量解...

3928
来自专栏我的博客

if和else匹配问题以及switch问题

$b = 1; $a = 2; if ($a > 1) { echo ‘1’; if ($b > 2) { echo ‘2’; } } else ...

35511
来自专栏微信公众号:Java团长

图说Java —— 理解Java机制最受欢迎的8幅图

下面的8幅图来自于 Program Creek 的 Java教程 ,目前这是该网站最受欢迎的文章. 希望本文能帮你回顾你已经知道的那些知识。如果图片讲解的不够清...

933
来自专栏Zephery

工厂模式

工厂模式 目录 何为工厂模式 工厂方法与抽象工厂 如何在Java EE中通过@Producers与@Inject注解实现工厂模式 如何创建自定义注解以及通过@Q...

42911

扫码关注云+社区

领取腾讯云代金券