|
半年之前,PM讓我在部門內部進行一次關于“內存泄露”的專題分享,我為此準備了一份PPT。今天無意中將其翻出來,覺得里面提到的關于CLR下關于內存管理部分的內存還有點意思。為此,今天按照PPT的內容寫了一篇文章。本篇文章不會在討論那些我們熟悉的話題,比如“值類型引用類型具有怎樣的區別?”、“垃圾回收分為幾個步驟?”、“Finalizer和Dispose有何不同”、等等,而是討論一些不同的內容。整篇文章分上下兩篇,上篇主要談論的是“程序集(Assembly)和應用程序域(AppDomain)”。也許有的地方說的不是很正確,希望讀者不吝賜教。
一、程序集與應用程序域
何謂程序集(Assembly)?它是一個托管應用的基本的部署單元。一個程序集是自描述的(通過元數據)、能夠實施版本策略和部署策略。我傾向于這樣的方式來定義程序集:“Assembly is a reusable, versionable, and self-describing building block of a CLR application.”從結構組成來看,一個程序集主要由三個部署組成:IL指令、元數據和資源。程序集的結構組成如下圖所示。
那么什么又是應用程序域呢?從功能上講,通過應用程序域實現的隔離機制為托管代碼的執行提供了一個安全的邊界。從與程序集的關系來講,我們可以將應用程序域看成是加載程序集的容器。只有相關的程序集被CLR加載到相應的應用程序域中,才談得上代碼的執行。
基于應用程序域的隔離,歸根結底是內存的隔離。一個基本的反映就是:在一個應用程序域中創建的對象,不能直接在另一個應用程序域中使用。這中間需要有一個基本的跨應用程序域傳遞的機制,我們將這種機制稱之為“封送(Marshaling)”。具體來講,又具有兩種不同的封送方式:按值封送(MBV:Marshaling By Value )和按引用封送(MBR:Marshaling By Reference)。MBV主要采用序列化的方式,而MBR最典型的就是.ENT Remoting。
二、系統程序域、共享程序域和默認程序域
當托管應用被啟動后,在執行第一句代碼之前,CLR會先后為我們創建三個應用程序域:系統程序域(System Domain)、共享程序域(Shared Domain)和默認程序域(Default Domain),它們分別具有不同的作用。
- 系統程序域:系統程序域是第一個被創建的應用程序域,同時也是其他兩個應用程序域的創建者。在該程序域初始化過程中,由它將msCorLib.dll這個程序集(這是一個很重要的程序集,.NET類型系統最基本的類型定義其中)加載到共享程序域中。此外,駐留的字符串也被保存在此系統程序域中。系統程序域的一個主要的任務是追蹤其他所有應用程序域的狀態,并負責加載和卸載它們;
- 共享程序域:共享程序域主要用于保存以“中立域(Domain-neutral Domain )”加載的程序集容器。所謂“中立域 ”方式加載的程序集,就是說程序集并不被加載到當前的程序域中并被該程序域專用,而是加載到一個公共的程序域中被所有程序域共享。
- 默認程序域:我們的托管程序最終就運行在該程序域中,默認程序域可以通過System.AppDomain表示。
三、字符串的駐留
上面的文字描述實際上透露一些重要的信息,其中一個就是字符串的駐留(String Interning)。關于字符串的駐留,我想大家都不陌生,所以在這里我就不作重復的介紹了。在這里,我只想討論一個問題:字符串的駐留是基于整個進程的,而不是僅僅基于某個應用程序域。
從上面的描述我們知道,字符串對象和一般的引用類型對象具有很大的不同:字符串對象直接被保存到系統程序域中,而一般的引用類型對象我們都是最終保存在GC堆中。從某種意義上講,在字符串駐留機制下,字符串也是以“中立域”的方式被加載的,被駐留的字符串能夠被同一個進程下所有應用程序域所共享。
那么,我們是否可以通過一些比較直觀的方式來驗證這一點。但是,我們不能直接編寫程序來比較兩個應用程序域中字符串是否是相同的引用,但是我們有一些間接的機制。我個人喜歡采用的方式是:加鎖。我們在運行于不同的應用程序域的代碼中對兩個字符串變量進行加鎖,如果程序運行的結果和對相同的對象加鎖一樣,那么就可以證明被枷鎖的兩個對象實際上是同一個對象。
為了便于演示,我寫一個如下一個AppDomainContext,表示某個AppDomain對應的執行上下文。AppDomainContext具有一個只讀的類型為AppDomain的屬性,該屬性通過構造函數執行,最終在靜態方法NewContext被創建。我們調用Invoke方法讓指定的方法對應的應用程序域中執行。
1: public class AppDomainContext
2: {
3: public AppDomain AppDomain { get; private set; }
4: private AppDomainContext(AppDomain appDomain)
5: {
6: this.AppDomain = appDomain;
7: }
8: public static AppDomainContext NewContext(string friendlyName)
9: {
10: return new AppDomainContext(AppDomain.CreateDomain(friendlyName));
11: }
12:
13: public void Invoke<T>(Action<T> action) where T : MarshalByRefObject
14: {
15: T instance = (T)this.AppDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName);
16: action.Invoke(instance);
17: }
18: }
NET技術:關于CLR內存管理一些深層次的討論 [上篇],轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。