【.Net基礎一】 類型、對象、線程棧、託管堆運行時的相互關係

目前在看CLR via C#,把總結的記下來,索性就把他寫成一個系列吧。
1.【.Net基礎一】 類型、對象、線程棧、託管堆運行時的相互關係
2.【.Net基礎二】淺談引用類型、值類型和裝箱、拆箱html


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函數,直接執行內存塊中的代碼。ide

寫博客效率好低(Orz)。。。函數


類型、對象、線程棧、託管堆運行時相互關係

下文講到對象類型對象要注意區分編碼

首先進程運行時,會在託管堆上建立一個System.Type的類型對象(文章後邊解釋)。而後進程中的線程建立時會分配一個1MB大小的棧。
線程

先上一段代碼(在書中代碼基礎上進行修改)3d

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中。
code

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

相關文章
相關標籤/搜索