文档章节

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

蓝宇
 蓝宇
发布于 2017/04/09 14:54
字数 1363
阅读 120
收藏 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
07/17
0
0
类型,对象,线程栈,托管堆在运行时的关系,以及clr如何调用静态方法,实例方法,和虚方法

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

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

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

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

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

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

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

北风其凉
2014/07/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

arts-week10

Algorithm 905. Sort Array By Parity - LeetCode Review Who’s Afraid of the Big Bad Preloader? 一文读懂前端缓存 一个网络请求3个步骤:请求,处理,响应,而前端缓存主要在请求处响应这两步...

yysue
今天
4
0
00.编译OpenJDK-8u40的整个过程

前言 历经2天的折腾总算把OpenJDK给编译成功了,要说为啥搞这个,还得从面试说起,最近出去面试经常被问到JVM的相关东西,总感觉自己以前学的太浅薄,所以回来就打算深入学习,目标把《深入理...

凌晨一点
今天
5
0
python: 一些关于元组的碎碎念

初始化元组的时候,尤其是元组里面只有一个元素的时候,会出现一些很蛋疼的情况: def checkContentAndType(obj): print(obj) print(type(obj))if __name__=="__main__": tu...

Oh_really
昨天
6
2
jvm crash分析工具

介绍一款非常好用的jvm crash分析工具,当jvm挂掉时,会产生hs_err_pid.log。里面记录了jvm当时的运行状态以及错误信息,但是内容量比较庞大,不好分析。所以我们要借助工具来帮我们。 Cras...

xpbob
昨天
162
0
Qt编写自定义控件属性设计器

以前做.NET开发中,.NET直接就集成了属性设计器,VS不愧是宇宙第一IDE,你能够想到的都给你封装好了,用起来不要太爽!因为项目需要自从全面转Qt开发已经6年有余,在工业控制领域,有一些应用...

飞扬青云
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部