阅读《CLR via C#》总结(第三天)

4 篇文章 0 订阅

接上文叙述,阅读《CLR via C#》总结(第二天)中理解了局部变量,实参,形参在运行时于调用栈的相互关系,那么接下来探究一下类型对象与堆之间的交互关系。

定义如下两个内部类:

internal class Employee
{
    public Int32 GetYearsEmployed() {...}
    public virtual string GetProgressReport() {...}
    public static Employee LookUp(String name) {...}
}

internal sealed class Manager:Employee
{
    public override String GetProgressReport() {...}
}

 我们在这里定义一个M3方法,以此方法中的运行来观察

void M3()
{
    Employee e;
    Int32 year;
    e = new Manager();
    e = Employee.LookUp("Joe");
    year = e.GetYearsEmployed();
    e.GetProgressReport();
}

在调用M3方法前,已经加载到进程中,堆已经初始化,线程栈已创建,马上调用M3方法。 

JIT编译器会先确认该方法引用到的类型(Employee,Manager,Int32,String) 的所有程序集都已加载,然后利用程序集的元数据,CLR提取与这些类型有关的信息创建数据结构来表示类型本身。因string和Int32非常常用,假定都已创建,我们只对Employee和Manager类型对象进行讨论。

 前面说过,堆上的所有对象都包含两个额外成员:类型对象指针和同步块索引,类型内部有静态数据字段,此外还包含一个放发表,每个方法都有对应的记录项。

构造manager对象时,CLR会自动初始化“类型对象指针“指向该对象引用的类型对象,并在调用构造方法之前初始化同步块索引,并将对象的所有字段初始化为0或null。

new操作符返回Manager对象的内存地址,该内存地址变量保存到对应声明的对象e上(e是在线程栈上的)。

 

接下来执行到e=Employee.Lookup("Joe");查找一个对象,并将对象地址返回到e变量上,调用静态方法(最上方有代码定义)时,CLR会定位与定义类型对应的类型对象,JIT编译器查找对应的记录项,对方法进行编译(如果需要的话),最后调用编译好的代码进行查找,找到后会在堆上定义一个新的Manager对象,用Joe的信息初始化它并将此对象的地址返回到变量e上。

 可以看到e此时不再引用前一个Manager对象,GC会自动将他回收。

接下来执行e的GetYearsEmployed方法,此方法是非虚实例方法,JIT会找到e的定义类型为Employee,e此时会定义成它的定义类型Employee并查找Employee类型对象中是否有定义GetYearsEmployed方法,如果没有会回溯类的层次结构(即查找它的父类,能回溯的原因是因为上面e引用的是一个manager类型对象)。将获取的值返回给栈上的year变量上。

最后执行e.GetProgressReport方法,调用虚方法时,我们会发现Manager与Employee类都有这个方法,则此时调用是看上面对e的赋值,如果lookup方法当时返回的是manager对象则调用Manager类型方法,否则调用Employee类的方法。

最后考虑一下Employee和Manager类型对象在最初创建的时候是如何初始化的?

Employee和Manager这类自己定义的类型对象在一个进程运行时会立即视为MSCorLib.dll中的System.Type类创建的特殊对象,他们的类型对象指针会初始化成对System.Type类型对象的引用。

另外System.Type也是对象所以它的类型对象指针会指向它自己。

由此,我们可知,System.Object中的GetType方法返回存储在对象的类型对象指针成员中的地址,可以判断任何对象的真实类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值