在 .NET 的运行时(CLR)中,MethodTable 和 EEClass 是描述类型的两个核心数据结构。它们分工明确:一个侧重于执行效率,另一个侧重于逻辑描述。
简单来说,它们的关系就像是“快速运行时手册”与“详细存档档案”。
1. MethodTable (方法表)
MethodTable 是 CLR 中最频繁使用的结构。每一个加载到 AppDomain 中的类型(Type)都有且只有一个 MethodTable。
-
存储位置:GC 堆(对于引用类型,对象的实例头之后紧跟着的就是指向 MethodTable 的指针)。
-
主要职责:它是运行时访问类型信息的“入口”,旨在实现高性能的成员访问。
-
包含信息:
-
方法槽位 (Method Slots):存储虚函数、接口方法的函数指针。这是实现多态的关键。
-
类型基本信息:包含组件大小、标志位(是否是值类型、是否有终结器等)。
-
GC 信息:辅助垃圾回收器识别对象中的引用字段。
-
指向 EEClass 的指针:指向更详细的元数据结构。
-
接口映射 (Interface Map):记录该类型实现的接口及其方法位置。
-
2. EEClass (Execution Engine Class)
EEClass 是对 CLI 元数据的运行时封装。由于元数据通常很大且不经常改动,CLR 将其拆分出来以减小 MethodTable 的体积,节省内存。
-
存储位置:LoaderHeap(类加载堆)。
-
主要职责:存储不需要在每次方法调用或对象创建时访问的静态、冷数据。
-
包含信息:
- 字段描述符 (Field Descriptors):记录字段的名称、类型和偏移量。Property(属性)的存储形式为Field+Method(get&set)
- 方法描述符 (Method Descriptors):记录方法的具体签名和实现细节。
- 层次结构:指向父类的 EEClass 以及兄弟类的引用。
- 元数据标记 (Token):对应编译后 PE 文件中的 Metadata Token。
3. 核心区别对比
| 特性 | MethodTable (热数据) | EEClass (冷数据) |
|---|---|---|
| 访问频率 | 极高。每次虚方法调用、类型转换、GC 扫描都会用到。 | 较低。通常仅在类加载、反射或 JIT 编译时用到。 |
| 主要内容 | 虚方法表、GC 布局、接口映射。 | 字段列表、方法元数据、类继承树。 |
| 目的 | 优化运行时执行速度 (Runtime Performance)。 | 减少内存占用,管理逻辑元数据。 |
| 数量关系 | 一个类型对应一个 MethodTable。 | 与 MethodTable 是一对一关系。 |
为什么不合并?
这种“冷热分离”的设计是为了性能。如果把所有元数据(EEClass 里的内容)都塞进 MethodTable,那么每个对象的类型描述符都会变得异常臃肿,破坏 CPU 缓存局部性,拖慢最常用的方法调用速度。