標籤:cache 啟動過程 通過 ocr 建立 cee for 線程 pre
寄宿
寄宿是指讓其他應用程式(Unmanaged 程式碼)使用CLR的能力,比如自己用C++開發的表單能建立CLR執行個體。
Managed 程式碼也能調用Unmanaged 程式碼
- [DllImport("kernel32.dll")]
- public static extern int WinExec(string exeName, int operType);
通常會調用win32 api,但是要查文檔才知道怎麼定義extern方法
CLR實際上被實現為COM伺服器,可以通過CoCreateInstance
或CLRCreateInstance
(推薦)建立CLR COM伺服器執行個體,而宿主就能使用CLR的東西。
CLRCreateInstance
在MSCorEE.dll
裡面(在安裝.Net Framework時被到系統目錄中),他的工作決定建立哪個版本的CLR(這個dll與.Net一起安裝,但卻是唯一的,安裝了多個版本的.Net電腦中這個dll總是最新版本)
無論是託管程式還是非託管程式,他們都會編譯成PE檔案(儲存程式的自描述資訊,比如入庫函數),而託管程式開始執行時會有一條JMP指令跳轉到MSCorEE.dll裡,通過MSCorEE.dll的PE檔案資訊找到這個_CorExeMain
函數的入口地址,然後修改剛才的JMP指令要跳轉的地址,從而將控制跳轉到了_CorExeMain這個函數裡面去。隨後CLR被啟動,接著根據託管程式的CLR表頭找到入口地址,再跳轉進去,程式開始運行
AppDomain
程式集邏輯容器
有點像進程,提供的資料和代碼隔離,但建立的代價比真正建立進程小。
- 不能訪問其他AppDomain建立的對象
- 可卸載
- 可單獨配置
- 這些配置涉及搜尋路徑,版本繫結重新導向,載入程式集的方式
- 可單獨保護
- CLR啟動時會建立一個預設的AppDomain
- AppDomain中有個Loader堆,記錄該AppDomain建立以來訪問過了哪些類型(類型對象)
- 頻繁被使用的類比如Object,它的類型對象會儲存在特殊的AppDomain中被其他AppDomain共用,代價是這個AppDomain永遠不會被卸載
- 注意多個AppDomain是用同一個託管堆
跨程式域通訊
AppDomain的核心是隔離但是它還是提供了跨AppDomain訪問對象的方法
假設一個情景,在AppDomain A中去訪問AppDomain B的對象
- AppDomain appDomainB = AppDomain.CreateDomain("NewDomain");
- DemoClass obj;
- // 在新的應用程式定義域中建立對象
- obj = (DemoClass)appDomainB.CreateInstanceAndUnwrap("ClassLib", "ClassLib.DemoClass");
這裡會拋出異常:"ClassLib.DemoClass"未標記為可序列化
其實這個過程叫做按值封送
,這個過程中會建立兩個對象,在AppDomain B中建立一個,然後序列化成位元組數組,傳給AppDomain A,再還原序列化成對象,這種做法要求DemoClass必須被打上可序列化特性。
還有一種封送方式叫按引用封送
,這種方式要求DemoClass派生自MarshalByRefObject
還是用上面一樣的代碼,這個過程本質是建立了兩個對象,在AppDomain A中建立了個代理對象obj,AppDomainB也建立個對象(這是真正的對象),在B向A發送對象引用前會做以下操作:
- 在AppDomain A的Loader堆中定義一個代理對象的類型對象(有與AppDomain B的ClassLib.DemoClass類的類型對象完全一樣的執行個體成員:方法,屬性,事件,但不包括執行個體欄位)
- 實際上內部會通過代理類找到真正的對象,再用反射獲得欄位值
- 定義代理類對象自己的執行個體欄位(儲存了那個AppDomain擁有真實的對象,以及如何找到他)
訪問對象時(這是個同步的過程),利用代理類的資訊,切換線程到B,獲得真正的對象,調用真正的方法
卸載AppDomain
通過AppDomain.Unload方法
過程:
- 掛起執行Managed 程式碼的所有線程
- 檢查與被卸載AppDomain有關係的線程,強迫它拋出異常ThreadAbortException
- 如果拋出的異常沒被處理,異常會被CLR”吞噬”,線程終止,進程繼續執行
- 找到所有建立的代理對象,給它們設定個flag,之後所有嘗試調用代理對象方法的操作都會拋異常
- 強制啟動記憶體回收
- 恢複剩餘線程
監視AppDomain
通過修改AppDomain的靜態屬性MonitoringEnabled為true來啟動監視,一旦啟動就不能關閉.
啟動後可以讀取下面4個屬性
- MonitoringSurivedProcessMemorySize 當前CLR執行個體控制的所有AppDomain使用的位元組數
- MonitoringTotalAllocatedMemorySize 已指派的位元組數
- MonitoringSurvivedMemorySize 正則使用的位元組數
- MonitoringTotalProcessorTime CPU佔用率
異常通知
通過給AppDomain.FirstChanceException事件註冊方法能在AppDomain拋異常時接到通知
這個時機是:在異常拋出後尋找catch塊前,FirstChanceException不能處理異常,AppDomain拋異常後會尋找catch,如果找不到則將異常拋給調用該AppDomain的AppDomain(中間有跨AppDomain傳遞),如果一直到線程棧頂都找不到catch塊,CLR將被終止
宿主如何使用AppDomain
- winform,wpf,控制台都是自寄宿(self-host)的,在本文開頭說過,啟動exe時會載入
MSCorEE.dll
緊接著啟動CLR,初始化後再運行Main方法
- Asp.Net中當使用者請求aspx時會被iis轉交給aspnet_isapi.dll(Unmanaged 程式碼),它負責啟動CLR,之後Asp.Net判斷該Web應用是否是第一次被請求,如果是則建立AppDomain,並以虛擬根目錄來標識。所以預設情況下,一個進程是可以跑多個網站(AppDomain)。
- Asp.Net的一個亮點是允許不關閉伺服器的前提下動態更改網站代碼,當Asp.Net檢測出檔案被修改後則會卸載就的AppDomain並建立新的AppDomain(載入新的檔案),為了確保這一過程順利執行,Asp.Net使用了一個名為shadow copying1的功能
資料
- 什麼是COM
- CLR寄宿(上) MSCOREE.DLL
- http://m.blog.csdn.net/zlbcdn/article/details/70195565
- .Net,你為什麼會慢 (託管程式與非託管程式啟動過程)
- <<.Net之美>>
TODO
- .net Romoting(不同電腦,不同進程,跨AppDomain訪問對象技術)
- SQLServer可以用Managed 程式碼寫預存程序?
- 在載入程式集時,先把程式集複製到Cache目錄下,再載入,這樣原程式集不會被鎖定。 ?
CLR via C#讀書筆記 CLR寄宿和AppDomain