一、在程序運行時由CLR管理內存分配(Memory Allocation),程序啟動時,操作系統會為每一個線程申請一個獨立的棧內存,用於存儲方法的局部變量、參數和返回值等;CLR會為進程申請一個連續的內存空間作為托管堆內存,用於存儲引用類型對象和類型對象等;
1.托管堆主要包含兩部分:存儲引用類型對象的GC堆(GC Heap)和存儲類型對象的加載堆(Loader Heap),其中GC堆分為小對象堆(Small Object Heap,SOH,<85000byte的對象)和大對象堆(Larage Object Heap,LOG,>=85000byte的對象);加載堆不受GC控制,生命周期從創建到應用程序域被卸載;
2.CLR在申請托管堆內存時,會維護一個指向下一個對象內存地址的指針,當在托管堆中分配新對象時,會通過該指針添加值來為對象分配所需的內存,因此在托管堆中分配內存和在棧內存中分配內存的速度基本一樣快;
二、在棧上分配值類型的變量時,如果值類型所占的空間不足當前系統的位數時,會分配當前系統位數的內存空間,例如在64位系統上分配的int類型的變量占用64位,即8個字節;對於引用類型地址也是這樣,即32位系統上引用類型地址占用4個字節,64位系統上則占用8個字節;
//在64位系統上測試: unsafe { int num1 = 10; int num2 = 20; MyClass myClass = new MyClass(); int num3 = 30; Console.WriteLine((int)&num1); Console.WriteLine((int)&num2); //與num1地址相差8個字節 Console.WriteLine((int)&num3); //與num2地址相差16個字節 }
三、在使用運算符new創建引用類型對象或裝箱操作等在托管堆上創建對象時,CLR所做的主要操作有:
1.計算類及所有基類中聲明的所有實例字段所需要的字節數,還有兩個開銷成員(Overhead Member)的字節數:同步塊索引(Sync Block Index)和類型對象指針(Type Object Pointer);
※同步塊索引為線程同步提供支持,也被稱為對象頭字節(Object Header Word);
※類型對象指針存儲該對象的類型對象所在的內存地址,也被稱為方法表指針(Method Table Pointer);
2.從托管堆中分配計算所得到的字節數,所有字節初始化為0;
3.初始化對象的類型對象指針和同步塊索引;
4.調用對應的實例構造函數,初始化實例字段,執行自定義構造函數中的其它操作;
※優先調用直接基類中的實例構造函數,直接基類中的實例構造函數又會調用其直接基類中的實例構造函數,最終最先調用的是基類System.Object中的實例構造函數;
5.返回新建對象的引用;
注:32位系統中,同步塊索引和類型對象指針分別占4個字節,占用的總空間大小會進行4字節倍數的對齊,同時即使類型定義中沒有實例字段,也會至少占用4個字節,即最小占用內存空間12字節;
注:64位系統中,同步塊索引和類型對象指針分別占8個字節,占用的總空間大小會進行8字節倍數的對齊,同時即使類型定義中沒有實例字段,也會至少占用8個字節,即最小占用內存空間24字節;
注:CLR在托管堆中連續分配多個對象時,這些對象在內存中也是連續存儲的;
注:可以使用WinDbg查看具體的內存分配情況;
注:CLR高度優化了托管堆上內存的分配和釋放,大多數情況下,在堆內存上分配類實例與在棧內存上分配結構實例的性能並無顯著差異;
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的認可是我寫作的最大動力!
作者:Minotauros
出處:https://www.cnblogs.com/minotauros/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。