.NET 类型、对象、线程栈和托管堆在运行时的相互关系

首先先看定义的两个类

如下:

 

线程栈执行顺序【主要观察一个方法调用】:

Windows进程已启动,CLR已加载到其中,托管堆已初始化,而且已创建一个线程(连同它的1MB栈空间)。线程已执行了一些代码,马上就要调用M3方法。如下图:

 

 

 

JIT编译器将M3的IL代码转换成本机CPU指令时,会注意到M3内部引用的所有类型,CLR要确认定义了这些类型的所有程序集都已加载。利用程序集的元数据,CLR提取这些类型的有关信息创建System.Type 对象

 

 

 

1、创建System.Type 对象

Type类【类型元数据信息】有关Type类的重要解释:

 

 

System.Type对象包含:

  1. 类型对象指针【初始化为System.Type类型对应的System.Type对象
  2. 同步块索引
  3. 静态字段
  4. 方法表

堆上所有对象都包含两个额外成员:类型对象指针(type object pointer)同步块索引(sync block index)

2、“序幕”代码,CLR为局部变量分配内存。并自动初始化为null或0【使用未显示初始化的局部变量会报错】。

 

3、显示初始化引用类型局部变量【创建对象】。

通过构造方法 new 对象,返回对象地址给局部变量。
类型对象指针【CLR会自动初始化,即对应的System.Type对象
同步块索引【调用构造前,CLR会初始化】
实例字段【调用构造前,CLR将实例字段设为null 或 0】


4、执行静态方法

CLR定位System.Type对象,JIT编译器在System.Type对象的方法表中找到对应方法的记录项,对方法进行编译(如果需要的话),最后调用JIT编译好的代码。


5、调用非虚方法

JIT编译器找到调用的变量的类型对应的System.Type 对象,如果对应System.Type对象的方法表中没有正在调用的那个方法,JIT编译器会回溯类层次结构(一直回溯到Object),并在沿途的每个类型中查找该方法。之所以能回溯,是因为每个System.Type对象都有一个字段引用了它的基类型。
JIT编译器对方法进行JIT编译(如果需要的话),再调用JIT编译好的代码。


6、调用虚方法

(实际类型中有override重写此虚方法,如果没有override默认是new修饰的,override修饰表现为运行时类型方法特征,具体可参考C# 虚方法、非虚方法、重写override、new,具体调用哪个方法分析


如果静态方法返回的是Employee对象:
如果静态方法返回的是Manager对象:
JIT编译器要在方法中生成一些额外的代码:方法每次调用都会执行这些代码。这些代码首先检查发出调用的变量,并跟随地址来到发出调用的对象【实际类型】。找到对应的System.Type对象中的调用方法的记录项,对方法进行JIT编译(如果需要的话),在调用JIT编译好的代码。

System.Object的GetType 方法返回存储在指定对象的“类型对象指针”成员中的地址。也就是说,GetType方法返回指向对象的System.Type的对象 的指针。这样就可以判断系统中任何对象(包括类型对象本身)的实例类型。


查看基类型的Type对象,一般使用 typeof运算符,因为GetType方法是System.Object的方法,会发生装箱,降低效率。

 

最后,堆中创建的对象“类型对象指针”成员的初始化,初始化成对应System.Type对象的引用:

这里称 “System.Type类型对应的System.Type对象”叫做TT,TT是一个对象,TT内部也有“类型对象指针”成员,这里是初始化为TT本身的引用,因为TT本身是一个System.Type对象。

 

posted @ 2020-07-22 19:25  好Wu赖  阅读(206)  评论(0编辑  收藏  举报