.Net AppDomain詳解(一)
AppDomain是CLR實現代碼隔離的基本機制。 每一個AppDomain可以單獨運行、停止;每個AppDomain有自己默認的異常處理; 一個AppDomain的運行失敗不會影響到其他的AppDomain。CLR在被CLR Host(windows shell or InternetExplorer or SQL Server)加載後,要創建一個默認的AppDomain,程序的入口點(Main方法)就是在這個默認的AppDomain中執行。 1.AppDomain vs 進程
AppDomain被創建在進程中,一個進程內可以有多個AppDomain。一個AppDomain只能屬於一個進程。 2.AppDomain vs 線程
當然這個說法很可能是和CLR的實現相關的。 3.AppDomain vs Assembly
Assembly是.Net程序的基本部署單元,它可以為CLR提供用於識別類型的元數據等等。 Assembly不能單獨執行,它必須被加載到AppDomain中,然後由AppDomain創建程序集中的對象。 一個Assembly可以被多個AppDomain加載, 一個AppDomain可以加載多個Assembly。每個AppDomain引用到某個類型的時候需要把相應的assembly在各自的AppDomain中初始化。因此,每個AppDomain會單獨保持一個類的靜態變量。 4.AppDomain vs 對象
任何對象只能屬於一個AppDomain。AppDomain用來隔離對象,不同AppDomain之間的對象必須通過Proxy(reference type)或者Clone(value type)通信。 引用類型需要繼承System.MarshalByRefObject才能被Marshal/UnMarshal(Proxy)。
值類型需要設置Serializable屬性才能被Marshal/UnMarshal(Clone)。 5.AppDomain vs Assembly Code
AppDomain和程序集的源代碼是什麽關系呢?每個程序集的代碼會分別裝載到各個AppDomain中?
首先我們要把程序集分3類
1.mscorlib,這是每個.net程序都要引用到的程序集。
2.GAC,這個是強命名的公用程序集,可以被所有的.net程序引用。
3.Assembly not in GAC,這是普通的assembly,可以不是強命名,不放到GAC中。 啟動CLR,進入entry point時可以設置LoaderOptimization屬性: [LoaderOptimization(LoaderOptimization.MultiDomain]
static void Main()
{...} LoaderOptimization屬性可以設置三個不同的枚舉值,來設置針對前面說的三種程序集的代碼存放以及訪問方式。 LoaderOptimization Enumeration/Attribute
Value | Expected Domains in Process | Each Domain Expected to Run ... | Code for MSCORLIB | Code for Assemblies in GAC | Code for Assemblies not in GAC |
---|---|---|---|---|---|
SingleDomain | One | N/A | Per-process | Per-domain | Per-domain |
MultiDomain | Many | Same Program | Per-process | Per-process | Per-process |
MultiDomainHost | Many | Different Programs | Per-process | Per-process | Per-domain |
1.SingleDomain,由於只啟動一個AppDomain,那麽code就被直接裝載到了AppDomain中,訪問靜態變量更快捷。 2.MultiDomain,所有的Assembly代碼是進程級別的,因此所有的AppDomain只訪問一份代碼。這大大減少了程序占用的內存,但是由於程序集的靜態變量仍然在各個AppDomain中,因此代碼訪問靜態變量需要先得到AppDomain的引用再進行轉換,速度會受到 影響。 3.MultiDomainHost,只有GAC代碼是共享的,非GAC的Assembly依然會加載到被使用的AppDomain中,這樣提高了靜態變量的訪問速度,當然也增加了程序占用的內存。 不管是哪種方式,mscorlib始終是process級別的,即只有一份mscorlib代碼在內存中。
C#中動態加載和卸載DLL
在C++中加載和卸載DLL是一件很容易的事,LoadLibrary和FreeLibrary讓你能夠輕易的在程序中加載DLL,然後在任何地方 卸載。在C#中我們也能使用Assembly.LoadFile實現動態加載DLL,但是當你試圖卸載時,你會很驚訝的發現Assembly沒有提供任何 卸載的方法。這是由於托管代碼的自動垃圾回收機制會做這件事情,所以C#不提供釋放資源的函數,一切由垃圾回收來做。
這引發了一個問題,用Assembly加載的DLL可能只在程序結束的時候才會被釋放,這也意味著在程序運行期間無法更新被加載的DLL。而這個功能在某 些程序設計時是非常必要的,考慮你正在用反射機制寫一個查看DLL中所有函數詳細信息的程序,程序提供一個菜單讓用戶可以選擇DLL文件,這時就需要讓程 序能夠卸載DLL,否則一旦用戶重新得到新版本DLL時,必須要重新啟動程序,重新選擇加載DLL文件,這樣的設計是用戶無法忍受的。
C#也提供了實現動態卸載DLL的方法,通過AppDomain來實現。AppDomain是一個獨立執行應用程序的環境,當AppDomain被卸載的 時候,在該環境中的所有資源也將被回收。關於AppDomain的詳細資料參考MSDN。下面是使用AppDomain實現動態卸載DLL的代碼,
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Reflection; namespace UnloadDll { class Program { static void Main(string[] args) { string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName; Console.WriteLine(callingDomainName); AppDomain ad = AppDomain.CreateDomain("DLL Unload test"); ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject"); obj.LoadAssembly(); obj.Invoke("TestDll.Class1", "Test", "It‘s a test"); AppDomain.Unload(ad); obj = null; Console.ReadLine(); } } class ProxyObject : MarshalByRefObject { Assembly assembly = null; public void LoadAssembly() { assembly = Assembly.LoadFile(@"TestDLL.dll"); } public bool Invoke(string fullClassName, string methodName, params Object[] args) { if(assembly == null) return false; Type tp = assembly.GetType(fullClassName); if (tp == null) return false; MethodInfo method = tp.GetMethod(methodName); if (method == null) return false; Object obj = Activator.CreateInstance(tp); method.Invoke(obj, args); return true; } } }
註意:
1. 要想讓一個對象能夠穿過AppDomain邊界,必須要繼承MarshalByRefObject類,否則無法被其他AppDomain使用。
2. 每個線程都有一個默認的AppDomain,可以通過Thread.GetDomain()來得到
更多:
C#如何測試代碼運行時間
C# 訪問操作註冊表整理
C#中如何獲取系統環境變量等
http://www.cnblogs.com/HopeGi/p/6422357.html
http://www.cnblogs.com/awpatp/archive/2009/11/24/1609570.html
.Net AppDomain詳解(一)