C#筆記 – AppDomain(應用域)
AppDomain
- AppDomain是一組程序集的邏輯容器
- CLR COM服務器初始化時會創建一個AppDomain(默認AppDomain),進程終止時銷毀
- 目的是為了提供「隔離」
- 具體功能
-
一個AppDomain的代碼不能直接訪問另一個AppDomain的代碼創建的對象
- 對象的生存期不能超過創建它的代碼所在的AppDomain
- 一個AppDomain要訪問另一個AppDomain的對象:
-
「按引用封送」
-
//按引用封送 AppDomain ad = AppDomain.CreateDomain("AD #1"); //切換至新Domain,然後在元數據中查找類型MarshalByRefType並構造 //返回的是一個「代理」,而不是真實對象 MarshalByRefType mbrt = (MarshalByRefType)ad .CreateInstanceAndUnwrap(Assembly.GetEntryAssembly().FullName, "MarshalByRefType"); mbrt.SomeMethod(); AppDomain.Unload(ad); public class MarshalByRefType : MarshalByRefObject //繼承自MarshalByRefObject { public MarshalByRefType() { } public void SomeMethod() { } public MarshalByValType MethodWithReturn() { return new MarshalByValType(); } }
-
-
「按值封送」
-
//按值封送 ad = AppDomain.CreateDomain("AD #2"); //將對象序列化,加載到目標Domain後再反序列化 //返回副本的引用 mbrt = (MarshalByRefType)ad .CreateInstanceAndUnwrap(Assembly.GetEntryAssembly().FullName, "MarshalByRefType"); MarshalByValType mbvt = mbrt.MethodWithReturn(); mbvt.OtherMethod(); AppDomain.Unload(ad); [System.Serializable] public class MarshalByValType //可序列化 { public void OtherMethod() { } }
-
-
-
AppDomain可以卸載
- CLR不支持從AppDomain中卸載特定的程序集,但是可以讓CLR卸載一個AppDomain
-
AppDomain可以單獨保護
-
AppDomain可以單獨配置
-
卸載AppDomain
- Step
- CLR掛起進程中執行過托管代碼的所有線程
- CLR檢查所有線程棧,查看哪些線程正在執行要卸載的AppDomain中的內容
- 導致線程展開
- 執行所有finally塊
- 確定所有線程離開AppDomain後,CLR遍歷堆,為引用了「由已卸載」AppDomain創建的對象的每個代理對象設置一個標志
- 表示它們引用的真實對象已經不在了,成為了「無效代理對象」
- CLR強制GC
- 恢復剩餘進程執行
監視AppDomain
- 一旦開啟就不能關閉
- 接口:
- MonitoringSurvivedProcessMemorySize
- 返回由當前CLR實例控制的所有AppDomain使用的字節數
- MonitoringTotalAllocatedMemorySize
- 返回特定AppDomain已分配字節數
- MonitoringSurvivedMemorySize
- 返回特定AppDomain當前正在使用的字節數
- MonitoringTotalProcessorTime
- 返回特定AppDomain的CPU占用率
- 返回字節數的接口只保證在上一次GC時是準確的
- MonitoringSurvivedProcessMemorySize
Windows進程中的AppDomain與CLR

- Windows進程運行著一個CLR COM服務器(執行引擎)
-
- CLR管理著1個或以上的AppDomain - 每個AppDomain有自己的Loader堆 - 每個Loader堆記錄了自AppDomain創建以來已訪問過的類型 - Loader堆中每個類型對象(Type1/Type2)都有一個方法表 - 方法表中的每個記錄項都指向JIT編譯的本機代碼(方法至少執行過1次) - 每個AppDomain加載了一些程序集 - 如果兩個AppDomain都加載了同一個程序集(如圖中的System.dll),並使用了來自System.dll的一個類型,那麼兩個AppDomain的Loader堆會為相同的類型分別分配一個類型對象 - 類型對象的內存不會由兩個AppDomain共享 - 而調用方法時的IL代碼經JIT編譯後,生成的本機代碼也單獨與各自的AppDomain關聯 - AppDomain中立 - 有的程序集本來就要由多個AppDomain使用,如MSCorLib.dll - CLR初始化時,會自動加載該程序集,所有AppDomain共享該程序集中的類型 - 為減少資源消耗,該程序集會以一種「AppDomain中立」的方式加載 - CLR會為AppDomain中立方式加載的程序集維護一個特殊的Loader堆 - 該Loader堆中的所有類型對象、類型定義的方法JIT編譯生成的本機代碼都會由進程中的所有AppDomain共享 - 這些程序集無法卸載,只有在終止Windows進程時由Windows去回收資源
可執行應用程序(exe)對AppDomain的使用
- 起動
- Windows用托管exe文件初始化進程時,會加載「墊片」(MSCorEE.dll)
- 「墊片」檢查程序集的CLR頭信息,決定加載哪個版本的CLR到進程
- CLR加載後,CLR頭再次被檢查,查找並執行應用程序的入口方法
- 運行
- 代碼運行時會訪問其他類型,引用另一個程序集的類型時,CLR定位所需的程序集,將其加載到同一個AppDomain中
- 入口方法返回後,Windows進程終止
參考書目
- 《CLR via C#》(第4版) Jeffrey Richter