堆與棧都是記憶體空間的一部分,其中,堆又可以分為托管堆和非託管堆。託管堆和棧由 CLR 管理。
對託管堆中的一部分——GC 堆中不用的物件進行釋放就是垃圾回收的主要工作,而託管堆的其他部分,和開發者關係相對沒有那麼大。對非託管堆的管理則需要由開發者完成。
在 CLR 開始執行第一行程式碼之前,它會先建立三個程式域:系統域、共用域以及預設的一個應用程式域(AppDomain 型別的一個範例)。
其中,開發者無法直接作業系統域和共用域,但 AppDomain 型別的範例可以有多個。
對於簡單的程式,例如控制台程式,第一個預設域的名稱就是執行檔案的全名,例如 abc.exe。可以使用 CreateDomain 方法建立更多的應用程式域。
每一個 AppDomain 的範例都有自己的載入堆,下面就會介紹載入堆到底是什麼。
堆
這裡的堆(heap)是託管堆(managed heap)的簡稱。顧名思義,它由 CLR 進行管理。
它是在執行程式時,CLR 申請的一塊記憶體空間。它基於進程,屬於進程記憶體空間的一部分。
這塊空間可以劃分為下面幾個主要部分:
-
(至少)三個程式域,以及它們自帶的載入堆和其他零部件。
-
GC 堆(GC heap):垃圾收集器的處理物件。它分為 0, 1, 2 代三塊區域,越高代的堆大小越大。
大物件堆(large object heap)是 2 代堆的一部分,它存放超過 85KB 大小的物件。
因為大物件堆裡面的物件太大,移動代價過高,所以微軟設計的意圖是直接將它提升到 2 代,避免升代移動引起的效能損失。
其中,載入堆(loader heap)存在於每一個程式域中,存放 CLR 自己的型別系統以及使用者定義的型別物件。
不同域的載入堆存放的物件不同。另外,AppDomain 的載入堆也存放靜態物件,由於靜態物件是全域性的,不會成為垃圾,所以載入堆不受垃圾收集器管轄。
載入堆又可以分為高頻堆(大小為 32KB),低頻堆(大小為 8KB)等。顧名思義,高頻堆存放的是 CLR 認為存取次數可能較多的物件,例如型別方法表等。
下圖簡單展示了託管堆的結構。