[讀書心得] .NET中 類型,對象,線程棧,托管堆在運行時的關系


.NET中 類型,對象,線程棧,托管堆 在運行時的關系

The Relationship at Run Time between Types,Objects,A Thread's Stack,and The Managed Heap for .NET

by 唐小崇

http://www.cnblogs.com/tangchong

  .NET中的類型,無論是值類型或引用類型都是繼承自Object的類。這點跟Java類似,但與C/C++有很大不同。既然值類型與引用類型都是類,那它們的沒有什么不同的地方。而最值得關注的不同就是:值類型對象的值直接存儲在線程棧中,引用類型對象的值存放在托管堆中,它的引用存放在線程棧中。本篇博文就談談CLR的線程棧,托管堆策略。

  當我們執行一個.NET程序, CLR  會加載一個新的進程,這個進程可能會(按照程序的編寫需求)包含多個線程。當一個線程被創建,它會申請一個 1MB大小棧(stack)。這個棧是以高位到地位保存的。線程棧是用來保存本地變量,參數,返回地址的,稍后會有詳細解釋。同時CLR會創建一個托管堆。(由於內容太多,先從線程棧開始講起,稍后再講托管堆)

現在,讓我們通過兩個例子方法M1,M2來了解線程棧與類型對象的關系:

我們先看看線程棧的樣子。圖1為一個線程棧的示例,我們假設該程序已經執行了一部分代碼,並即將執行M1方法。

圖1:線程棧示例

線程棧有以下作用:

  ·保存向某個方法傳遞的參數(passing arguments);

  ·保存當前執行的方法內的本地變量(local variales);

  ·保存當前執行方法的返回地址(return address);

所有的方法都會包含一些 序幕代碼(prologue code)和 收場代碼(epilogue code) ,序幕代碼用來初始化一個方法,這些代碼把本地變量,參數,返回地址壓入線程棧中。而收場代碼會在函數執行完畢后會清理給該函數分配的空間,並將指令指針(CPU's instruction pointer)指向返回地址(即指向該函數的調用者),從而釋放線程棧空間。

當我們開始執行M1,CLR將 本地變量name、向M2傳遞的參數s 和 返回地址 壓入線程棧中,如圖2所示:

 圖2:將本地變量name、向M2傳遞的參數s和返回地址壓入線程棧中

接着我們執行到M2(name);CLR開始調用M2方法,這時序幕代碼會在線程棧里分配兩個本地變量 length,tally,如圖3所示。當M2方法執行完畢后,收場代碼清理M2分配的空間,並將指令指針指向返回地址(即M1)。此時,我們的線程棧又回到圖2所示的狀態。

 

圖3:M2方法開始執行時線程棧的情況

以上,就是整個線程棧的執行情況。

我們接着來看看CLR中的托管堆。說到托管堆,就不得不提引用類型。下面我們列兩個示例類,Employee和它的子類Manager:

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所示:我們現在加入一個新方法M3。當我們的程序開始執行,CLR 會初始化線程棧和托管堆。

首先JIT編譯器將M3方法的中間代碼(IL)JIT編譯為本地指令(native CPU instructions)。

然后CLR檢測M3引用的所有類型(本例中為Employee,Int32,Manager,String),這時CLR會確保提供這些類型的程序集已經被加載(否則,會報錯)。

最后CLR提取有關這些類型信息創建一些數據結構(Type Object)來表示類型本身。也就是說,CLR會先創建 Type Object,然后通過它來創建類的實例對象。

圖4:當M3方法被調用。CLR在托管堆中創建對應的類的類型對象(Type Object)

在創建好的類型對象 Type Object(並不是類的實例對象)中包括以下成員

  類型對象指針(Type object ptr)

  同步塊索引(Sync block index)

  靜態成員(Statoc fieds)

  方法列表(method table)

緊接着,當CLR確認M3所需求的所有的類型對象(Type Object)都被創建,則開始執行M3的本地代碼。如圖5所示,序幕代碼在線程棧中創建本地變量並將它們初始化為null或者0。

圖5 序幕代碼在線程棧中創建本地變量

程序開始執行e = new Manager();

如圖6所示,這行代碼指示CLR在托管堆中創建一個Manager類型的實例對象(instance)。該實例對象包含: 類型對象指針、 同步塊索引以及 該類型中的定義成員(包括它的父類成員,在本例中父類為Employee,Object)。

然后,CLR會自動將該實例對象(Manager Object)的 類型對象指針(Type object ptr)指向相應的類型對象(Manager Type Object),此外,CLR會初始化同步塊索引和所有成員為0並調用構造器(構造函數)。

最后new 操作符將創建好的實例對象在托管堆中的地址 返回並賦值給線程棧中的引用類型變量e。

圖6:創建並初始化一個Manager實例對象

下面我們看看3種不同的方法:靜態方法,非虛方法,虛方法的執行情況。

下一行代碼是e = Employee.Lookup("Joe"); 這里調用了Employee的靜態方法Lookup()。

如圖7所示,當調用一個靜態方法,首先JIT編譯器會定位到該方法對應的類型Type Object對象(本例中為Employee Type Object)。然后將該Type Object函數列表中的對應方法(本例中是Lookup)JIT編譯,並執行。

我們假設Joe存在並且是一個經理,則Lookup函數會在托管堆中創建一個Manager實例對象,並用Joe初始化它。最后將這個Manager實例對象的地址返回,賦值給e。

圖7:靜態函數Lookup被調用,創建並用joe初始化一個Manager 對象,並賦值給e

繼續執行下一行代碼:year = e.GetYearsEmployed();

如圖8所示:當我們調用一個非虛函數(nonvirtual instance method),JIT編譯器會定位到 調用者的類型(e的類型為Employee)對應Type Object中(本例中為Employee Type Object)。在該Type Object中的方法列表中,查找對應方法(如果沒找到,會向其父類尋找直到Object為止)。JIT編譯該方法,並執行。我們不妨假設joe已經工作5年了。則Employee 的 GetYearsEmployed方法返回5,並賦值給線程棧中的year變量。

圖8:調用Employee的非虛函數 GetYearsEmployed。

接下來一行代碼是e.GetProgressReport();該方法是一個虛方法(virtual instance method)。

調用一個虛方法前,JIT編譯器會額外的執行一些代碼。這些代碼會先查找到 調用該方法的變量(e)所指向的實例對象(在本例中,即為用Joe初始化的Manager實例對象)。接下來,檢查該對象中的類型對象指針,以找到對象的真正類型type object(本例中為Manager Type Object)。最后JIT編譯該type object方法列表中的對應方法,並執行。

如圖9所示,JIT編譯並執行的是 Manager Type Object中的GetProgressReprot方法,而不是Employee Type Object中的。

圖9:調用虛函數GetProgerssReport(),實際類型Manager的type Object中的方法被執行

至此,示例代碼結束。我們討論了調用靜態方法,非虛函數,虛函數的三種情況。但我們還有一點沒有完成。

我們會注意到在Type Object中也有Type object ptr,這是因為這些Type Object也是一個“類型”的實例對象。它們的類型比較特殊,叫做System.Type,定義在MSCorLib.dll中。當CLR開始執行一個進程,它會先為System.Type創建一個Type Oject,稱為Type Type Object。如圖10所示,本例的 Employee Type Object,Manager Type Object都是Type Type Object的“實例對象”。最后,Type Type Object 本身也是一個對象,它的Type object ptr 指向自身。這就是.NET 萬物皆對象的思想。

圖10:Emloyee和Manager的type objects 是 System.Type 類型的實例對象

 


免責聲明!

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



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