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


    目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧。
    1.【.Net基础一】 类型、对象、线程栈、托管堆运行时的相互关系
    2.【.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都是该类型的“实例”。因此他们的对象指针会指向System.Type的类型对象。
      因为System.Type的类型对象本身也是一个对象,内部也有类型对象指针,所以指针指向自身。
      顺便说一句,一个对象的GetType方法调用的是基类System.Object的方法。该方法返回的是当前对象的类型对象的指针,所以能够知道对象的真实类型。

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

       e = new Manager();
    

    这会在托管堆中创建一个Manager类型对象的一个实例。即Manager对象(注意区分和类型对象的区别)。
    该对象也包含类型对象指针和同步块索引。还包含必要的字节来容纳Manager类型定义的所有实例数据字段(包括基类字段)。
    CLR会自动初始化内容类型对象指针,让它引用Manger类型对象。也会初始化同步块索引,并将对象的所有实例字段设为Null或0,然后调用类型构造器(修改实例字段数据)。
    new 操作符返回Manager对象的内存地址,并将地址保存在变量e中。

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

    e = Employee.LookUp("ZhangSan");
    

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

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

       year = e.GetYearsEmployed();
    

    调用实例方法,JIT编译器会找到发出调用变量的类型(这里是e的类型Employee)的类型对象(Employee类型对象)。然后JIT查找记录项,对方法进行编译,执行CPU指令。
    如果Employee类型没有定义那个方法,则会沿着基类一直寻找,直到Object类型。之所以能沿途查找,是因为每个类型对象都有一个字段引用了它的基类型。
    假设该方法返回5,则year就会为5。

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

      e.GenProgressReport();
    

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

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

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

    基础太差,个人理解不知道是否有错误,有错误请指正,谢谢。
    看书不易,写博客也不易,该睡了。。。( ╯□╰ )

    转载请注明出处:http://www.cnblogs.com/xcong/p/4060781.html

  • 相关阅读:
    UVa532 Dungeon Master 三维迷宫
    6.4.2 走迷宫
    UVA 439 Knight Moves
    UVa784 Maze Exploration
    UVa657 The die is cast
    UVa572 Oil Deposits DFS求连通块
    UVa10562 Undraw the Trees
    UVa839 Not so Mobile
    327
    UVa699 The Falling Leaves
  • 原文地址:https://www.cnblogs.com/xcong/p/4060781.html
Copyright © 2020-2023  润新知