AppDomain 詳解(轉)

AppDomain是CLR的運行單元,它能夠加載Assembly、建立對象以及執行程序。

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 線程
其實二者原本沒什麼好對比的。AppDomain是個靜態概念,只是限定了對象的邊界;線程是個動態概念,它能夠運行在不一樣的
AppDomain。
一個AppDomain內能夠建立多個線程,可是不能限定這些線程只能在本AppDomain內執行代碼。
CLR中的System.Threading.Thread對象實際上是個soft thread,它並不能被操做系統識別;操做系統能識別的是hard thread。
一個soft thread只屬於一個AppDomain,穿越AppDomain的是hard thread。當hard thread訪問到某個AppDomain時,一個
AppDomain就會爲之產生一個soft thread。
hard thread有thread local storage(TLS),這個存儲區被CLR用來存儲這個hard thread當前對應的AppDomain引用以及soft
thread引用。當一個hard thread穿越到另一個AppDomain時,TLS中的這些引用也會改變。
固然這個說法極可能是和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()來獲得shell

相關文章
相關標籤/搜索