文档章节

C#类型、对象、线程栈、托管堆在CLR中的关系

蓝宇
 蓝宇
发布于 2017/04/09 14:54
字数 1363
阅读 154
收藏 0

假设一个已经加载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类型对象的类型对象指针是它自己。

 

© 著作权归作者所有

共有 人打赏支持
蓝宇
粉丝 1
博文 7
码字总数 5793
作品 0
呼和浩特
私信 提问
C# 类型、对象、线程栈和托管堆在运行时的关系

我们将讨论类型、对象、线程栈和托管堆在运行时的相互关系,假定有以下两个类定义: internal class Employee { public int GetYearsEmployed() { return 1; } public virtual string GetPro...

zhengwei_cq
2018/07/17
0
0
类型,对象,线程栈,托管堆在运行时的关系,以及clr如何调用静态方法,实例方法,和虚方法

1、线程栈 window的一个进程加载clr。该进程可能含有多个线程,线程创建的时候会分配1MB的栈空间。 如图: void Method() {   string name="zhangsan"; //name 被放入栈里面 Method2(name);...

张三你好
2018/08/16
0
0
值类型、引用类型和泛型的前世今生

值类型、引用类型和泛型 多语言 咱们先不说主题,先说说CLR支持多语言。 .net有个非常强大的特点,那就是跨语言,支持很多语言,比如C#、J#等。先来个图看一看 看到这个图,每个语言都有自己...

张先森!
2018/08/22
0
0
通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core?

什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先从类型系统开始讲起,我将通过跨语言操作这个例子来逐渐引入一系列.NET的相关概念,这主要包括:...

dotNET跨平台
2018/07/03
0
0
Perfmon.exe辅助检查.NET程序内存泄漏

因为工作用C#写的程序老是内存泄漏,在网上找了找资料后,发现了Windows自带的性能监视器Perfmon.exe可以辅助查看.NET程序的运行状况。今天研究了一番,下面的内容就是一些我认为比较重要需要...

北风其凉
2014/07/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

React SSR样式及SEO的实践

前一篇主要记录了一下SSR配置以及结合Redux的使用。这里简单说一下React SSR中样式处理和更优雅的SEO SSR样式 在React客户端渲染,添加样式很容易。写一个css样式文件,在对应组件中引用。标...

前端小攻略
7分钟前
0
0
华为手机太猛!余承东吹的牛今天都实现了

华为是世界上少有的在2B和2C领域同时取得成功公司。如今,华为消费者业务的营收,已经在华为总营收中占据“半壁江山”。 12月27日,华为董事长郭平在新年致辞中披露,预计2018年华为预计实现...

linux-tao
17分钟前
1
0
JSP的Model2模式

整体可以看做是 Request > Control > ( Service > DAO > POJO ) > Response POJO是数据实体类,最佳实践是与数据库物理表相对应,方便用工具进行生成,也易于理解。由于直接与物理表相对应...

max佩恩
26分钟前
0
0
rabbitMQ的使用

RabbitMQ介绍 RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种。 AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间...

狼王黄师傅
31分钟前
0
0
面向对象继承

第1章 面向对象 1.1 知识回顾 1.Java中的数据类型 2.引用数据类型String中的方法 3.IO流_读写文件 1.2 面向对象概念回顾 什么叫面向对象编程?有人是这么理解的 也有人说不对,面向对象编程是你...

stars永恒
38分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部