CLR via C#學習之線程棧,托管堆,值類型和引用類型


  最近在系統的讀CLR via C#這本書,發現寫得很好。但是抽象的概念比較多,有些地方理解起來表費勁耗時,所以在這里記錄下自己覺得重要的地方。

  本文要闡述的主要內容:在運行時,值類型和引用型是如何在線程棧和托管堆中工作的。

  

     線程棧的基本概念及資源分配:

  線程棧的創建:在windows進程加載完CLR,創建一個線程后,大小為1MB的線程棧被創建。

  線程棧的作用存儲形參和局部變量。如圖所示name和m2方法的形參將會被存放在線程棧中。

  疑問為什么全局變量不存放在線程棧中?全局變量應該是在類里面,類作為引用類型自然是存放在托管堆中。

  

 

  圖4-3 執行String name="Joe"后,CLR會在線程棧中name分配空間。

  

  圖4-4

  1.執行M2(name)之前,CLR將name的拷貝s作為實參,存放在線程棧中。

  2.執行M2(name)之前,CLR將函數的執行返回地址(return address)存放在線程棧中,以便M2方法執行完成好,回到M1中繼續執行。

  

  

  圖4-5

    1.M2方法內的局部變量存放在線程棧中。

    2.M2方法執行完成后,清空M2中局部變量,同時取出return address,回到M1方法中繼續執行。此時線程棧中的資源只剩下name了,如圖4-3所示。

  

  

  托管堆中的資源分配

  我們有兩個類定義如下:

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()  { ... } 
}

  圖4-6 windows進程啟動,CLR加載完成,托管堆完成初始化,線程被創建,且執行了一部分代碼,接下來准備執行M3方法。

  

  

  

 

  圖4-8

  1.在實行M3方法之前,JIT編譯器將IL代碼編譯成本地CPU指令。

  此外JIT編譯器還做了的兩件事情:

  (1)通過查詢元數據信息,加載M3方法中引用的所有類型的程序集(如果程序集已經加載,就跳過)。

  (2)通過查詢元數據信息,在托管堆中創建引用的類。

  2.執行M3中申明變量e和year代碼時,在線程棧中分配兩個變量。

  

  圖4-9

  1.執行e=new Manager()時,根據元數據找到對應的模板(Manager Type Object),然后創建實例對象Manager object,並且修改線程棧中變量e的值,讓它指向Manager object對象。

  2.對象指針指向相應的類型對應指針。

  3.由圖中可以看出對象在托管堆中包括三部分:對象指針,同步索引塊,對象字段。

  疑問1:對象的方法存放在哪里?由圖可以看出方法存放在類中。

  疑問2: 實例的方法存放在類對象中,多線程訪問實例的方法的過程是什么樣子的?

  答:假設方法已經編譯成IL指令集合(包括指令1,指令2,指令3)。線程1進入方法訪問完指令1,2后,保存線程現場。線程2開始訪問指令1,2,此時CPU的權限又移交給線程1,線程1恢復線程現場,接着執行指令3完成后,線程1回到線程池或者關閉。CPU把控制權交給線程2,繼續執行指令3,完成后線程3回到線程池。

  疑問2的解答是我個人理解,如果不正確,歡迎指教。

 

  圖4-10

  同上一步類似。JIT編輯器講LookUp方法編譯成IL指令,並執行指令,在托管堆中創建Joe對象。

 

  圖4-11 JIT編譯器將GetYearsEmployed編譯成CPU指令,並執行后,將值存放在線程棧的Year變量中。

 

  圖4-12

  (1)執行e.GenProgressReport方法時,難點在於是執行基類還是子類中的方法。

  (2)對於虛方法的執行,需要檢查變量類型(Employee)和托管堆類型(Manager)是否一致,如果不一致,需要做額外的處理。

  (3)不一致的話,需要通過對象指針定位到類型對象,然后找到對應的方法。因為Manage中改方法前面是Override,所以找到的是Manager類中的GenProgressReport方法。

  疑問:如果Manger中的GenProgressReport方法前面是用new修飾的,托管堆的圖該怎么畫?e.GenProgressReport方法應該如何執行?

  答:1)類對象(Manager Type Object)應該生成兩個同名方法,並且在元數據中做好標記(該方法是否為子類中的方法)。

    2)執行e.GenProgressReport方法時,因為類對象中有兩個方法,根據引用變量e的類型為基類,應該調用基類中繼承過來的同名方法。

  疑問2:類A繼承類B,里面的方法,相同的方法在內存中是一塊還是兩塊?

  

  答:這個問題不是本文重點,將在下一篇博客中解決。

  圖4-13 Employee和Manager的類型對象是System.Type類型的實例。所以在CLR加載完成,托管堆初始化后,立馬在托管對中創建System.Type類空間。

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM