C#类型、对象、线程栈、托管堆在CLR中的关系
博客专区 > 蓝宇 的博客 > 博客详情
C#类型、对象、线程栈、托管堆在CLR中的关系
蓝宇 发表于7个月前
C#类型、对象、线程栈、托管堆在CLR中的关系
  • 发表于 7个月前
  • 阅读 33
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 学生专属云服务套餐 10元起购>>>   

假设一个已经加载CLR组件的一个windows进程。该进程可能有多个线程。线程创建时会开辟栈空间。栈用于向方法传递实参,方法内部定义的局部变量也在栈上。

下图展示了线程的栈内存。栈从高位内存地址到地位内存地址构建。图中线程已经执行了一些代码,栈顶已经有些数据了,现在假设执行的代码调用了F1方法。

 

1. 首先它在线程栈上分配局部变量name的内存.

2. 然后F1调用F2方法,将局部变量name作为实参传递。name的局部变量地址被压入栈。

3. 调用方法有时会将返回地址压入栈

4. 在线程栈位length和tally分配内存。

5. 最终,F2抵达return,造成CPU指令指针被设置成栈的返回地址。F2的栈帧展开----出栈,栈的状态恢复到1的情况。

6. 最终F1会返回它的调用者。它的返回地址应该在1的上面。

 

现在讨论下一个话题

 

假定有以下两个类的定义:

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(){...}        

}

 

注意:堆上所有对象都包含两个额外的成员:类型对象指针和同步块索引。

其中对象指针指向类别.

同步块当使用lock锁定对象时,对象的同步块被设置一个整数,这个整数就是同步块的数组索引。初始情况,同步块索引位负数,当lock结束时,同步块索引又重新被设置成负数。

上图线程已经执行了一些代码,马上就要调用F3方法。

1. 首先JT编译器将F3的IL代码换成CPU指令时,会注意到F3内部引用的所有类型是否全部加载,然后利用程序集的元数据,提取预支相关的信息,创建一些数据结构来表示类型本身。

2. 由于线程已经执行了一些代码。所以不妨认为Int32 和String类对象已经创建好了。

3. 由于Employee定义了三个方法,Manager只定义了一个方法,所以Manager的方法表中只有一个记录项。

4. 当CLR确定方法需要所有的类型对象都已经创建,F3的代码编译后就允许线程执行F3的代码,在代码执行时就会对线程栈中的局部变量进行内存分配,CLR会将局部变量初始为null或0.

5. F3构造一个Manager对象引用

6. 局部变量year

7. 执行new Mnager。在托管堆上创建Mnager类型的一个实例,也就是一个Manager对象,和所有对象一样Mnager对象也有类型对象指针和同步块索引,该对象还要包含必要的字节来容纳Manager类型定义的所有实例数据字段,以及任何基类的所有实例字段,任何时候在堆上新建对象,CLR都会自定初始化内部的类型对象指针来因用对象对应的类型对象。此外在调用类型的构造函数之前,CLR会初始化同步块索引,并将对象所有实例字段设为null或0.new 操作符返回Manager对象的内存地址,将地址保存到局部变量e中。

8. 下一行代码调用Employee的LookUp方法,在调用静态方法时,CLR会定位与定义静态方法的类型对应的类型对象,然后,JIT会在类型对象的方法表中查找与被调用方法对应的记录项,进行JIT编译(如果需要),然后调用编译好的代码,在调用此方法时,会在堆中构造一个新的Manager,e不再引用第一个Manager对象。

9. 下一行调用Employee的非虚实例方法GetYearsEmployee。调用非虚方法时,JIT会找到发出调用的哪个变量e的类型,这时e被定义成Employee,如果Employee没有此方法,JIT会一直回溯直到Object或找到那个方法。,之所以可以如此回溯,是因为每个类型对象都有一个字段引用了它的基类型。

10. F3的下一行调用Employee的虚方法GetProgressReport。调用虚实例方法时,JIT要在方法中生成一些额外的代码。方法每次调用都会执行这些代码,这些代码首先检查发出调用的变量,并跟随地址检查类型对象指针成员,该成员指向对象的实际类型,然后在实际类型的方法表中找到记录项。

 

注意:Employee和Manger类型对象都包含类型对象指针。这是因为类型对象本质上也是对象。CLR创建类型时,必须先初始化这些变量。CLR在运行时,会理解为MSCorLib.dll 中定义的System.Type类型创建一个特殊的类型对象。上述两个类型对象都是它的实例。所以它们的类型对象指针是System.Type类型对象的引用,而System.Type类型对象的类型对象指针是它自己。

 

共有 人打赏支持
粉丝 2
博文 7
码字总数 5793
×
蓝宇
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: