先前我們提到我們撰寫的.NET相關程式都是透過通用執行環境(CLR)來幫我們執行,以C#為例C#編譯器會把我們撰寫的C#程式碼編譯成中繼語言(IL),而CLR會讀取IL並透過即時編譯(just-in-time,簡稱JIT)機制轉換成對應平台的機器碼。而上述這一整個過程中我們所撰寫的C#程式其實經過了兩次編譯,第一次由C#編譯器進行編譯,第二次由JIT進行編譯。而最開始C#編譯器進行編譯的過程我們稱為「編譯時期」,而到CLR執行我們的程式(包含JIT編譯)我們稱呼為「執行時期」。
以下是編譯到執行的簡略步驟:
- C#編譯器幫我們撰寫的C#程式進行編譯
- 過程中會進行語法檢查與型別變數的安全檢查(例如避免int存取double發生型別不一致)
- 編譯器會編譯出IL與程式相關資料(稱為中繼資料,一種表示程式本身方法、類別、變數等自我描述資料)包裝成exe或dll檔等這類Windows可執行檔(PE檔)
- PE檔的開頭會要求Windows把程式執行權轉交給MSCorEE.dll(.NET Framework的檔案)
- MSCorEE.dll的CorExeMain函式會建立通用運行環境(CLR)
- CLR會看相關資料準備需要的內容,例如會用到.NET類別庫的那些功能。
- 當執行到正要用的功能時進行JIT編譯轉成機器碼。
即時(just-in-time)編譯機制
.NET Framework有提供三種JIT機制:
- 預設JIT:會針對執行時的平台作指令集優化,將IL轉成適當的機器碼,並以方法為單位當正要執行該方法才進行編譯,編譯的過程中會檢查IL碼安全性,並把編譯後的機器碼暫存於記憶體中,待第二次執行該方法時使用,程式結束後則清除(再次執行程式則需要重新編譯)。
- EconoJIT:相對第一種,這是節省記憶體資源的JIT機制,編譯後的機器碼使用一次後就會從記憶體中清除。
- Install-time code generation:相對第一種,在要執行時會將所有程式全部直接編譯成機器碼,減少等待JIT的次數。
組件(Assembly)
C#編譯器得到的.exe或.dll等可執行檔會視為一個組件(Assembly),組件是.NET程式的執行單位,也是我們.NET程式移植與部屬單位,一個組件可以拆分成不同組件或參考其他組件,例如一份專案可以拆分成不同專案或參考不同專案。其中組件的組成是由IL檔與中繼資料包成的模組(Module),一份組件可以包入一個模組或多個模組,也可以將圖片等資源視為模組包入組件。.NET類別庫也是由多個組件所組成的。
補充:.NET類別庫有些類別已經在執行前事先編譯成機器碼的形式,不完全都會經過JIT。
補充:微軟官方中文文件中.NET 中的組件因為機器翻譯文章中同時用元件與組件稱呼Assembly,我這邊則用組件來稱呼。