欢迎阅读本系列其他文章:

【读书笔记】.NET本质论第一章 The CLR as a Better COM
【读书笔记】.NET本质论第二章-Components(Part One)
【读书笔记】.NET本质论第二章-Components(Part Two,public key)
【读书笔记】.NET本质论第二章-Components(Part Three,CLR Loader)
【读书笔记】.NET本质论第二章-Components(Part Four,Assembly Resolver)
【读书笔记】.NET本质论第三章-Type Basics(Part 1)
【读书笔记】.NET本质论第三章-Type Basics(Part 2)
【读书笔记】.NET本质论第三章-Type Basics(Part 3)
【读书笔记】.NET本质论第四章-Programming with Type(Part One)

 

本章的第一篇简单的说了一下值类型和引用类型,以及编写代码时对对象日后在内存中布局的影响。本篇会重点关注对象加载到内存后是个什么样子,是个什么样的结构,而这些结构在运行时又起到什么作用。

下图是一个对象在内存中的结构:

【读书笔记】.NET本质论第四章-Programming with Type(Part Two)

所有引用类型,分配在托管堆上时,都会分配额外的两个字段,同步块索引与方法表指针,在32位中,这两个字段各占4个字节。紧跟在方法表后面的就是该对象的实例字段。

关于同步块索引这个字段,我已经用了三篇文章来介绍了,在这里就不再重复了:

揭示同步块索引(上):从lock开始

揭示同步块索引(中):如何获得对象的HashCode

揭示同步块索引(下):总结

那本篇文章就主要讨论这个方法表指针以及相关的东西。

读过《.NET本质论》的读者也许会发现,原书说的在MethodTable Pointer的地方是htype,也就是所谓的“类型句柄”,那类型句柄和方法表指针又是什么关系呢?这个在文章稍后再做探讨。我们在这里就先只关注方法表指针和方法表。

其实方法表(MethodTable)这个名字并不能表达出它本身的含义,可以说还让人产生误解。实际上方法表根本就不是一个“表”,从主体的结构上也没有看到任何表的意思(实际上所谓的方法的列表时附加在MethodTable主体结构之后的,要访问方法表,只需要用this+offset,这样做纯粹是为了性能的考虑,因为一个类型的方法个数不定,常规做法肯定是在另外一个地方动态的开辟一块内存保存这个方法的列表,然后在MethodTable内包含一个指向这个列表的指针,因为这个方法列表在方法调用的时候需要频繁使用,性能非常重要,如果使用指针的方法,就多了一层,是间接的访问,而现在这种设计只需要加一个偏移就够了),关于方法表结构的更详细描述在后面的文章中,下图是一个简图:

【读书笔记】.NET本质论第四章-Programming with Type(Part Two)

方法表实际上包含的是对一个类型的详细描述,比如该类型的父类,接口,当然还包括该类型的方法。实际上和方法表共存的还有一个东西:EEClass。EEClass和方法表所要表示的东西一样,都是根据类型的元数据构建而来。那为什么要分两个东西呢?对于一个类型的元数据来说,有一部分是经常用到的,比如Virtual Disaptch,这些数据称之为“Hot Data”,还有一部分却不经常使用,这部分就放在EEClass中,而方法表中有指向EEClass的指针。但是方法表和EEClass并不是一一对应的关系,比如一个泛型类型,如果泛型的参数是引用类型的话(比如MyClass<string>和MyClass<Object>),它们的EEClass是共享的,而方法表却不同。

方法表在多态、类型检查、JIT中都起着非常重要的作用,是.NET的核心之一。当第一次执行某方法时,CLR会检查该方法内要使用的所有类型,如果这些类型没有加载则会加载这些类型,加载类型的时候还要看看该类型的程序集是否加载了,如果没加载则首先加载程序集,然后读取程序集中的元数据,先构建EEClass,然后构建方法表。在AppDomain中这样的俩个堆:High-Frequency-Heap和Low-Frequency-Heap,方法表就会加载到High-Frequency-Heap,EEClass加载到Low-Frequency-Heap。注意,这两个Heap都不属GC的管辖,所以一个类型加载后,不会因为不再使用而被清理,只会等到AppDomain卸载后才会清理。对于普通的WinForm程序,默认情况下只有等到程序关闭的时候AppDomain才会Unload,对于ASP.NET或寄宿在其他Host中的程序(比如Sql Server 2005开始提供对CLR的支持),这个就要看Host到底是如何调度的。

上面光用文字说了这么多,下面就用Visual Studio+SOS建立一些感性的认识,为后面根据Rotor的代码分析方法表建立基础。

Demo1:

namespace Yuyijq.Study.MethodTable
   2: {
interface IFoo
   4:     {
void Run();
   6:     }
class Foo : IFoo
   8:     {
int _instanceField = 5;
int _staticField = 6;
  11:  
#region IFoo Members
  13:  
void Run()
  15:         {
  16:             
  17:         }
  18:  
#endregion
  20:     }
class Program
  22:     {
string[] args)
  24:         {
new Foo();
  26:             f.Run();
  27:  
  28:             Console.ReadLine();
  29:         }
  30:     }
  31: }

相关文章: