• .Net 类型、对象、线程栈、托管堆运行时的相互关系


    JIT(just in time)编译器

    接下来的会讲到方法的调用,这里先讲下JIT编译器。以CLR书中的代码为例(手打...)。以Main方法为例:

    static void Main(){
       Console.WriteLine("Hello");
       Console.WriteLine("XiaoCong");
    }
    1. 首先CLR会检测到Main方法中引用了一个Console类型,然后CLR会为其分配一个内部结构。
    2. 内部结构中每个方法都对应一个记录项,记录项中都容纳一个地址,根据此地址可以找到方法的实现。
    3. 对结构进行初始化时,会把记录项指向JITCompiler函数。

    4. 当第二次执行wirteLine时,由于第一次已经进行了验证和编译,所以跳过JIT函数,直接执行内存块中的代码。

    写博客效率好低(Orz)。。。

    类型、对象、线程栈、托管堆运行时相互关系

    下文讲到 对象类型对象 要注意区分

    首先进程运行时,会在托管堆上创建一个System.Type的类型对象(文章后边解释)。然后线程创建时会分配一个1MB大小的栈。

    先上一段代码(在书中代码基础上进行修改)

    internal class Employee{
      public			Int32 GetYearsEmployed(){...}  //非虚实例方法(实例方法)
      public virtual String GenProgressReprot(){...}//虚实例方法(虚方法)
      public static Employee LookUp(String name){...}//静态方法
    }
    internal sealed class Manager:Employee{
      public override String GenProgressReport(){...}//重写方法(虚方法)
    }
    
    
    void M1(){
      String name="XiaoCong";
      M2(name);
      ...
      return
    }
    
    void M2(String str){
      Employee e;
      Int32 year;
      e = new Manager();
      e = Employee.LookUp("ZhangSan");
      year = e.GetYearsEmployed();
      e.GenProgressReport();
    }

    1.首先执行M1方法,会在线程栈上创建1MB的栈空间,然后会加入 序幕 代码和 结尾 代码

    序幕代码:进行一些初始化变量操作(初始化null或0)

    结尾代码:压入【返回地址】

    2.接着执行M2方法,将参数和返回地址,以及局部变量压入栈

    3.CLR要确保程序集已经全部加载,然后创建M2内部引用对象的数据结构(类型对象)

    1. 数据结构包含类型对象指针、同步块索引、静态数据字段(包括基类中字段,字节数由对象自身中分配)、方法表。
    2. (String和Int32比较常用,可能实际编码过程中已经创建好了,就不在图中显示了。)
    3. Manager和Employee的 类型对象 指针指向Type。Type指向自身。
      类型对象本质也是对象,CLR创建这些对象时,必须初始化这些程序员。CLR在一个进程中运行时,就会立即创建一个特殊的System.Type类型的类型对象。
      Employee和Manager都是该类型的“实例”。因此他们的对象指针会指向Type。

    4.然后M2方法会执行构造Manager对象

    C# e = new Manager();

    这会在托管堆中创建一个Manager类型对象的一个实例。即Manager 对象 (注意区分和类型对象的区别)。

    该对象也包含类型对象指针和同步块索引。还包含必要的字节来容纳Manager类型定义的所有实例数据字段(包括基类字段)。

    CLR会自动初始化内容类型对象指针,让它引用Manger类型对象。也会初始化同步块索引,并将对象的所有实例字段设为Null或0,然后调用类型构造器(修改实例字段数据)。

    new 操作符返回Manager对象的内存地址,并将地址保存在变量e中。

    5.M2下一步调用静态LookUp方法

    C# e = Employee.LookUp("ZhangSan");

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

    6.M2下一步调用实例方法

    C# year = e.GetYearsEmployed();

    调用实例方法,JIT编译器会找到发出调用变量的类型(这里是e的类型Employee)的类型对象(Employee类型对象)。然后JIT查找记录项,对方法进行编译,执行CPU指令。

    如果Employee类型没有定义那个方法,则会沿着基类一直寻找,直到Object类型。之所以能沿途查找,是因为每个类型对象都有一个字段引用了它的基类型。

    假设该方法返回5,则year就会为5。

    7.M2下一步调用虚方法(被Manager重写)

    C# e.GenProgressReport();

    调用虚方法,JIT要在方法中生成一些额外代码。这些代码首先检查发出调用的变量,然后跟随地址找到发出调用的对象(这里是新的Manager对象)。接着代码对象内部“类型对象指针”,然后在类型对象(Manager类型对象)方法表中查找记录项,编译成成CPU代码。

    如果LookUp方法发现的是一个Employee类型,这里执行的就是Employee类型的GenProgressReport方法。

    8.执行完M2方法之后,会找到返回地址,返回M1方法中继续执行

    9.M1执行完成之后,会返回调用M1的方法继续执行

    文章来源:http://xcong.cnblogs.com

  • 相关阅读:
    Postmanapi接口自动化局限性
    爱加密加固产品原理分析_定制版
    白话文理解DCT离散余弦变换
    mysql like使用预编译
    【Jenkins】jenkins控制台/生成的日志文件 中文乱码
    【Python】连接Linux并执行命令
    【Java】时间转换(可用于计算耗时场景)
    【Java】List去重 / 删除ArrayList中重复元素,保持顺序 / 提取两个list中不同的元素
    【Java】读取文本文件
    【Python】文件转码/编码/解码/utf8
  • 原文地址:https://www.cnblogs.com/xiaoyaodijun/p/4661986.html
Copyright © 2020-2023  润新知