部落格園CLR基礎研究團隊|CLR團隊精品系列|Anytao技術部落格
[你必須知道的.NET]第十八回:對象建立始末(上)
發布日期:2007.12.3 作者:Anytao
2007 Anytao.com ,原創作品,轉貼請註明作者和出處。
本文將介紹以下內容:
1. 引言
瞭解.NET的記憶體管理機制,首先應該從記憶體配置開始,也就是對象的建立環節。對象的建立,是個複雜的過程,主要包括記憶體配置和初始化兩個環節。例如,對象的建立過程可以表示為:
FileStream fs = new FileStream(@"C:"temp.txt", FileMode.Create);
通過new關鍵字操作,即完成了對FileStream類型對象的建立過程,這一看似簡單的操作背後,卻經曆著相當複雜的過程和周折。
本篇全文,正是對這一操作背後過程的詳細討論,從中瞭解.NET的記憶體配置是如何?的?
2. 記憶體配置
關於記憶體的分配,首先應該瞭解分配在哪裡的問題。CLR管理記憶體的地區,主要有三塊,分別為:
· 線程的堆棧,用於分配實值型別執行個體。堆棧主要由作業系統管理,而不受垃圾收集器的控制,當實值型別執行個體所在方法結束時,其儲存單位自動釋放。棧的執行效率高,但儲存容量有限。
· GC堆,用於分配小對象執行個體。如果參考型別對象的執行個體大小小於85000位元組,執行個體將被分配在GC堆上,當有記憶體配置或者回收時,垃圾收集器可能會對GC堆進行壓縮,詳情見後文講述。
· LOH(Large Object Heap)堆,用於分配大對象執行個體。如果參考型別對象的執行個體大小不小於85000位元組時,該執行個體將被分配到LOH堆上,而LOH堆不會被壓縮,而且只在完全GC回收時被回收。
本文討論的重點是.NET的記憶體配置機制,因此下文將不加說明的以GC堆上的分配為例來展開。關於實值型別和參考型別的論述,請參見[第八回:品味類型---實值型別與參考型別(上)-記憶體有理]。
瞭解了記憶體配置的地區,接著我們看看有哪些操作將導致對象建立和記憶體配置的發生,關於執行個體建立有多個IL指令解析,主要包括:
· newobj,用於建立參考型別對象。
· ldstr,用於建立string類型對象。
· newarr,用於分配新的數組對象。
· box,在實值型別轉換為參考型別對象時,將實值型別欄位拷貝到託管堆上發生的記憶體配置。
在上述論述的基礎上,下面從堆棧的記憶體配置和託管堆的記憶體配置兩個方面來分別論述.NET的記憶體配置機制。
2.1 堆棧的記憶體配置機制
對於實值型別來說,一般建立線上程的堆棧上。但並非所有的實值型別都建立線上程的堆棧上,例如作為類的欄位時,實值型別作為執行個體成員的一部分也被建立在託管堆上;裝箱發生時,實值型別欄位也會拷貝在託管堆上。
對於分配在堆棧上的局部變數來說,作業系統維護著一個堆棧指標來指向下一個自由空間的地址,並且堆棧的記憶體位址是由高位到低位向下填充。以下例而言:
public static void Main()
{
int x = 100;
char c = 'A';
}
假設線程棧的初始化地址為50000,因此堆棧指標首先指向50000地址空間。代碼由入口函數Main開始執行,首先進入範圍的是整型局部變數x,它將在棧上分配4Byte的記憶體空間,因此堆棧指標向下移動4個位元組,則值100將儲存在49997~50000單位,而堆棧指標表示的下一個自由空間地址為49996,:
接著進入下一行代碼,將為字元型變數c分配2Byte的記憶體空間,堆棧指標向下移動2個位元組至49994單位,值’A’會儲存在49995~49996單位,地址的分配
最後,執行到Main方法的右括弧,方法體執行結束,變數x和c的範圍也隨之結束,需要刪除變數x和c在堆棧記憶體中的值,其釋放過程和分配過程剛好相反:首先刪除c的記憶體,堆棧指標向上遞增2個位元組,然後刪除x的記憶體,堆棧指標繼續向上遞增4個位元組,程式執行結束,此時的記憶體狀況為:
其他較複雜的分配過程,可能在範圍和分配大小上有所不同,但是基本過程大同小異。棧上的記憶體配置,效率較高,但是記憶體容量不大,同時變數的生存周期隨著方法的結束而消亡。
未完待續:託管堆的記憶體配置機制和必要的補充說明,近期發布,敬請關注。
參考文獻
(USA)Joe Duffy, Professinal .NET Framework 2.0
(USA)Don Box, Essiential .NET
(MSDN)Hanu Kommalapati and Tom Christian, Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects, http://msdn.microsoft.com/msdnmag/issues/05/05/JITCompiler/default.aspx
溫故知新
[開篇有益]
[第一回:恩怨情仇:is和as]
[第二回:對抽象編程:介面和抽象類別]
[第三回:曆史糾葛:特性和屬性]
[第四回:後來居上:class和struct]
[第五回:深入淺出關鍵字---把new說透]
[第六回:深入淺出關鍵字---base和this]
[第七回:品味類型---從一般型別系統開始]
[第八回:品味類型---實值型別與參考型別(上)-記憶體有理]
[第九回:品味類型---實值型別與參考型別(中)-規則無邊]
[第十回:品味類型---實值型別與參考型別(下)-應用征途]
[第十一回:參數之惑---傳遞的藝術(上)]
[第十二回:參數之惑---傳遞的藝術(下)]
[第十三回:從Hello, world開始認識IL]
[第十四回:認識IL代碼---從開始到現在]
[第十五回:繼承本質論]
[第十六回:深入淺出關鍵字---using全接觸]
[第十七回:貌合神離:覆寫和重載]
2007 Anytao.com
原創作品,轉貼請註明作者和出處,留此資訊。
本文以“現狀”提供且沒有任何擔保,同時也沒有授予任何權利。
This posting is provided "AS IS" with no warranties, and confers no rights.