类型,对象,线程栈和托管堆在运行时的相互关系(一)中说到类型,对象,线程栈和托管堆在运行时的相互关系。下面围绕CLR再来讨论这个问题。定义如下两个类。
internal class Employee {
public public Int32 GetYearsEmployed() { ... }
public virtual String GenProgre***eport() { ... }
public static Employee Lookup(String name) { ... }
}
internal sealed class Manager : Employee {
public override String GenProgre***eport() { ... } public override String GenProgre***eport() { ... }
}
现在假如Windows进程已经启动,CLR已经加载到了进程中,如在钱一章所讲,托管堆已经初始化,而起创建了一个线程(连同1M的线程栈)。该线程已经执行了一些代码,马上就要执行函数M3了,现在的线程栈,托管堆和M3函数体如下图:
当JIT(即时编译器)将M3的IL代码转换成本地的CPU指令时,会注意到M3内部引用的所有类型:Employee,Int32,Manager,String。这个时候CLR要确保定义了这些类型的所有程序集都已经加载到堆中。然后,利用程序集的元数据,CLR提取这些类的信息,并创建一些数据结构来表示类型本身。下图展示Manager和Employee类型对象使用的数据结构。因为这个线程在调用M3方法之前已经执行了一些代码,而且Int32和String是很常用的类型。所以假设Int32和String类型对象已经创建。
(堆上的每个对象都有两个额外的成员:类型对象指针和同步块索引)。所以这两个对象都有这两个成员。
定义一个类型时,可以在类型的内部定义静态数据字段。为这写静态数据字段的地址是在类型对象自身分配的。在每个类型对象中,最后都包涵一个方法表。在方法表中每个方法都有一个对应的记录项。
现在,当CLR确定的方法需要的所有类型对象都已创建,而且M3的代码经过编译之后,就允许线程开始执行M3的代码,M3对"序幕"代码执行时,在线程战中为局部变量分配内存,而且会自动将局部变量初始化为NULL或者0。此刻线程栈,托管堆的情况如下图所示。
然后是本次的重点。
M3执行它的代码构造一个Manager对象。这就在托管堆中创建Manager类型的实例。创建完这个Manager对象以后,线程栈和托管堆的情款如下图:Manager对象也包涵类型对象指针和同步块索引两个成员。该对象还包括必要的字节来容纳Manager类型定义的所有实例数据字段,以及由Manager的任何基类定义的所有实例字段。在堆上创建一个对象,CLR都会自动初始化内部类型对象指针成员,让他引用与类型对象。然后CLR初始化同步块索引,并将对象的所有实例字段初始化为NULL或者0。然后调用类型的构造函数。改变类型内部实例字段。new 操作符返回的Manager对象在堆中的地址。该地址保存在e中。
下次说说调用对象方法。
参考CLR VIA C#
转载于:https://blog.51cto.com/fanjiaxing/1401028