类型基础

这篇我想解释的内容主要是关于类型、对象、线程栈以及托管堆在运行时候的相互关系。

我们都知道在编程语言进入某个方法时,大多数的做法都是在当前的线程栈当中将返回地址压入栈中,当方法运行完后再依次进行出栈直到最外层的调用。这样就能实现保存入口时的地址和程序进入方法前的状态。

.Net中也是这样实现的,现在有以下代码(摘自CLR via C# 第4版,第4章):

public class Employee{
   public         Int32 GetYearsEmployed(){...}
   public virtual String GenProgressReprot(){...}//虚方法
   public static Employee LookUp(String name){...}//静态方法
}
public class Manager:Employee{
   public override String GenProgressReport(){...}//重写方法(虚方法)
}

void M1(){
   String name="Jarvis";
   M2(name);
   //do something
   return
}

void M2(String str){
   Employee e;
   Int32 year;
   e = new Manager();
   e = Employee.LookUp("ZhangSan");
   year = e.GetYearsEmployed();
   e.GenProgressReport();
}

1.执行M1方法

首先开辟1MB的栈空间

CLR在执行M1的时候会先对所有的局部变量进行初始化并压入栈中。(null/0,所以如果试图使用未显示初始化的对象就会爆掉。)

将M2函数的参数和返回地址压入栈中。


name


str


(返回地址)

2.执行M2方法

为局部变量e、year分配空间。


name(string)


str(string)


(返回地址)


e(Employee)


year(int)

3.CLR利用程序集中的元数据,创建M2内部引用的数据结构来表示类型本身。(这里需注意类型对象和类型的实例对象的区别)

1.数据结构包括:类型对象指针(指向对象的类型对象)、同步块索引、静态字段、方法表。

2.Manager和Employee的类型对象指向Type。Type指向自身。

类型对象本质也是对象,CLR创建这些对象时会初始化这些成员。CLR在运行时会立即创建一个特殊的System.Type类型对象。Employee和Manager类型对象都是该类型的实例。因此,他们的类型对象指针才会被初识化指向System.Type。

System.Object的GetType方法返回的就是指向类型对象的指针。CLR以此实现判断系统中任何的对象。

4.创建Manager对象的实例

到了这一步才开始真正构造对象的实例

Manager也会先初识化对象指针指向它的类型对象和同步索引块、实例字段(0/null)。然后再运行对象的构造函数,最后new操作符返回对象地址并保存至变量e中。

5.调用静态的LookUp方法

调用静态方法时,CLR会定位到静态方法的类型的类型对象(Employee类型对象)。然后找到对应的方法表中的记录项,对方法进行JIT编译(第一次执行该方法),再调用JIT生成的CPU指令。假设该方法到数据库中查找ZhangSan,然后返回一个全新的Manager对象,LookUp方法就会在堆上构造一个全新的Manager对象,用ZhangSan的数据库信息初始化它。然后返回该对象的地址保存在变量e中。然后旧的Manager对象会等待垃圾回收器进行回收释放。

6.调用GetYearsEmployed方法

调用实例方法时,JIT会找到与“发出调用的那个变量(e)的类型(employee)”对应的类型对象(Employee类型对象)。如果Employee没用定义正在调用的那个方法,那么JIT会回溯类层次结构,并在沿途的每个类型中查找该方法。之所以能够这样回溯是因为每个类型对象都有一个字段引用了他的基类型。

7.调用虚方法GetProgressReport()

调用虚方法,JIT会在方法中生成一些额外的代码。

1.这些代码首先检查发出调用的变量(e)。

2.找到发出调用的对象(Manager)。

3.通过对象的“类型对象指针”来找到实际类型,在方法表中查找引用的方法。

4.由于e应用的是Manager对象,所以调用的是Manager的GetProgressReport对象,如果Lookup方法返回的是Employee对象的话,这边调用的就是Employ的GetProgreessReport对象了。

8.方法运行完,出栈至返回地址,返回上一个方法继续执行。

时间: 2024-11-10 15:22:51

类型基础的相关文章

C# 类型基础 值类型和引用类型

引言 本文之初的目的是讲述设计模式中的 Prototype(原型)模式,但是如果想较清楚地弄明白这个模式,需要了解对象克隆(Object Clone),Clone其实也就是对象复制.复制又分为了浅度复制(Shallow Copy)和深度复制(Deep Copy),浅度复制和深度复制又是以如何复制引用类型成员来划分的.由此又引出了引用类型和值类型,以及相关的对象判等.装箱.拆箱等基础知识.索性从最基础的类型开始自底向上写起. 值类型和引用类型 先简单回顾一下C#中的类型系统.C# 中的类型一共分为

C# 类型基础——你可能忽略的技术细节

引言 本文之初的目的是讲述设计模式中的 Prototype(原型)模式,但是如果想较清楚地弄明白这个模式,需要了解对象克隆(Object Clone),Clone 其实也就是对象复制.复制又分为了浅度复制(Shallow Copy)和 深度复制(Deep Copy),浅度复制 和 深度复制又是以 如何复制引用类型成员来划分的.由此又引出了 引用类型 和 值类型,以及相关的对象判等.装箱.拆箱等基础知识. 于是我干脆新起一篇,从最基础的类型开始自底向上写起了.我仅仅想将对于这个主题的理解表述出来,

第四章 类型基础

1. 概述 本章讲述使用 类型 和 CLR 时需要掌握的一些基础知识. 2. 名词解释 3. 主要内容 3.1 所有类型都从System.Object 派生 所有对象都用new操作符来创建,步骤如下: ① 计算类型及其所有基类型中定义的实例字段需要的字节数.包括两个额外成员:类型对象指针 和 同步块索引. ② 从托管堆中分配指定类型要求的字节数,从而分配对象的内存,分配的所有字节都设为零(0). ③ 初始化对象的 类型对象指针 和 同步块索引. ④ 调用类型的实例构造器,向其传入在对new的调用

C# 类型基础

引言 本文之初的目的是讲述设计模式中的 Prototype(原型)模式,但是如果想较清楚地弄明白这个模式,需要了解对象克隆(Object Clone),Clone其实也就是对象复制.复制又分为了浅度复制(Shallow Copy)和深度复制(Deep Copy),浅度复制 和 深度复制又是以 如何复制引用类型成员来划分的.由此又引出了 引用类型和 值类型,以及相关的对象判等.装箱.拆箱等基础知识. 于是我干脆新起一篇,从最基础的类型开始自底向上写起了.我仅仅想将对于这个主题的理解表述出来,一是总

NET CLR via C#(第4版)第4章 类型基础

本章内容: 1 所有类型都从System.Object派生 2 类型转换 3 命名空间和程序集 4 运行时的相互关系 本章讲述使用类型和CLR时需掌握的基础知识.具体地说,要讨论所有类型都具有的一组基本行为. 讨论类型安全性.命名空间.程序集.以及如何将对象从一种类型转换成另一种类型. 本章最后会解释类型.对象.线程栈和托管堆在运行时的相互关系. 4.1 所有类型都从System.Object派生 CLR要求每个类型最终都从System.Object类型派生.从而确保类每个对象都具备一组最基本的

【C#进阶系列】04 类型基础

关于System.Object 所有类型都从System.Object派生而来. System.Object的公共方法中ToString()一般是返回对象的类型的全名,只有Int32这些类型将其重写后,新方法才会返回其值的字符串表示. 其中还有两个受保护的方法: MemberwiseClone:深复制. Finalize:在垃圾回收器判断此对象应该被回收后,在对象的内存被实际回收前会调用此方法. 关于类型判断和转换: 用is来判断对象为某类型或者某类型的派生类,是为true,不是为false.

mysq字段类型基础

字段后面括号里的数字含义 对数字类型的字段,它能够存取的数字的范围,也就是大小是固定的,你怎么再后面括号里改都没有,括号里的数字是限制显示的长度的.主要是对某些值的宽度短于该列宽度的值进行左填补显示的. 当与可选的扩展属性 ZEROFILL 一起使用时,缺省填补用的空格被零代替.举例来说,一个列被定义为 INT(5) ZEROFILL,插入的值 4 被检索出来时为 00004 对于字符类型字段,其可限制字符长度,如varchar(8) 一般建表时,数字类型的字段直接选择类型即可,不需要指定宽度,

使用Swift操作NSDate类型基础

时间类型是我们在处理业务的时候使用非常频繁的一个数据类型.下面我们看一下时间NSDate的基本使用方法. 1.比较大小 我比较擅长.NET,我们知道C#里面DateTime类型可以使用">""<""="来直接判断.但是在Swift里NSDate是不支持这种比较的方式的.我们需要使用NSDate.Compare方法来比较.NSDate.Compare返回一个枚举NSComparisonResult.这个枚举包含3个值: NSCompar

第4章 类型基础 -- 4.1 所有类型都从System.Object派生

4.1 所有类型都从System.Object派生 “运行时”要求每个类型最终都从System.Object类型派生. 由于所有类型最终都从System.Object派生,所以每个类型的每个对象都保证了一组最基本的方法. System.Object类提供了如下表所示的公共实例方法: 表4-1 System.Object的公共方法 Equals 若两个对象具有相同的值,就返回 true .详情请参考"对象相等性和同一性" GetHashCode 返回对象的值的哈希码.如果某个类型的对象要

NET基础课-- 类型基础(NET之美)

1.类型:值类型  引用类型. 分类依据:类型在内存的分配方式.值类型在堆栈,引用类型在托管堆. 名词:栈--所有变量都会被分配在栈上,只不过值类型直接含有数据,引用类型含有一个指向托管堆对象的地址. 含有一个堆上对象的地址的变量叫变量指向此对象或变量引用此对象. 值类型:简单类型(基类库类型别名) int  byte等 声明一个int类型实际是声明一个system.int32的结构类型变量,此变量包含了值类型(此处为结构体)中所有字段 结构   枚举 引用类型:类  委托 接口等 2.变量初始