什么是CLR?
CLR,公共語言運行時(Common Language Runtime)是一個由多種語言使用的“運行時”。他的核心功能包括(內存管理、程序集加載、安全性、異常處理和線程同步),可以被面向CLR的所有語言使用。這里的“運行時”,就是一個運行時環境,就像JAVA虛擬機一樣。哦,錯了,確切的說是JRE(Java Runtime Enviromental)。JVM確切的說不是一個實體的java虛擬機,而是一個規范,就像CLI一樣。會有不同的實現,如:JRockit還是Hotspot(前者是Oracle的JVM商業實現,后者是Sun的開源實現——當然現在也是Oracle的)
什么是CLI?
公共語言基礎結構(Common Language Infrastructure),定義了構成.NET Framework基礎結構的可執行代碼,以及代碼運行時的環境規范。它定義了一個與語言無關的跨體系結構的運行環境,這使得開發者可以用規范內定義的各種高級語言來開發軟件,並且無須修正即可將軟件運行在不同的計算機體系結構上。CLI是一個開放型的技術規范,由微軟、惠普和英特爾於2000年向ECMA倡議的。說白了,CLI就是一套規范,CLR是對CLI的一種實現。那么CLI這份規范中具體定義了哪些內容呢?不妨去ECMA的網站下一份看看,也可以在這里下載:
如上面截圖所示:包含了6部分,:
Partition I: Concepts and Architecture –描述.NET CLI的全部體系結構,提供公共類型系統(CTS,Common Type System)、虛擬執行系統(VES,Virtual Execution System)和公共語言規范(CLS,Common Language Specification)的標准化描述,還提供對元數據(Metadata)的信息性描述。
通用類型系統(CTS):規范.NET中數據的類型。
元數據系統(Metadata):是.NET中描述數據的數據。
通用語言規范(CLS):描述多語言之間進行交互的語言規范,.NET系統包括的語言有C#、C++、VB、J#,它們都遵守通用語言規范。
虛擬執行系統(VES):是一個可運行受管理代碼(Managed Code)的運行環境,它提供了運行受管理代碼所需要的內置數據類型(data type),以及假定的機器型態與狀態設置、流程控制與例外處理等參數。
Partition II: Metadata Definition and Semantics - 提供元數據的標准化描述,包括元數據在.NET擴展PE文件格式中的位置(以.NET擴展PE文件格式的形式表示),元數據的邏輯內容(以表格及其關聯的集合的形式表示,實際上使用了形式化方法表示)和元數據的語義(以匯編成為虛擬機代碼的匯編器ilasm理解的形式表示)。
Partition III: CIL Instruction Set –描述CIL的指令集(注意,是CIL,不是CLI哦)
Partition IV: Profiles and Libraries- 提供CLI庫的簡要介紹,以及將其分解為Profile和庫的規范。這里有一個配套的文件CLILibraryTypes.xml,考慮過隨這一部分一起發布,不過該文件是XML格式的,該文件提供了CLI庫中每一個類、值類型和接口的細節說明。“Profile”一詞在這里的含義是庫的集合,一起組合起來構成提供一定功能級別的一致性整體,換而言之,不同的Profile對應不同的庫集合,提供的功能級別也不同。
Partition V: Debug Interchange Format- 描述了CLI 產品的調試交互的標志方式;
Partition VI: Annexes- 提供了一些用ILAsm(CIL Assembly Language)寫的例程等
什么是CTS?
CLR是完全圍繞類型展開的,通過類型,通過一種編程語言寫的代碼可以與另一種語言寫的代碼溝通。所以Microsoft制定了一個正式的規范,“通用類型系統”(Common Type System,CTS),它描述了類型的定義和行為。上面已經提到,Microsoft已經將CTS和,NET Framework的其他組件 -- 包括(文件格式、元數字、中間語言及對底層平台的)提交給ECMA完成標准化工作,最終形成的標准稱為“公共語言基礎結構”(Common Language Infrastructure)。
CTS里定義了以下內容:
什么是CLS?
CLR中集成了多種語言,一種語言中可以調用另一種語言創建的對象。為了實現這一目的,微軟定義了一個公共語言規范(Common Language Specification)。它詳細定義了一個最小的功能集。任何編譯器如果想生成類型兼容於其他“符合CLS,面向CLR語言”生成的組件,那么必須要支持這個最小功能集。下面用一張圖來說明彼此間的關系:
什么是IL?
先從上圖看一下.net的編譯過程,源代碼文件經過編譯器編譯后生成托管模塊(managed module)。托管模塊說白了就是一個32位(PE32)或64位(PE32+)的PE文件。
具體的結構如下:
PE32或PE32+文件頭 |
CLR頭 |
元數據 |
IL代碼 |
這里我們着重談談IL,IL是與CPU無關的機器語言,比大多數的CPU機器語言都要高級。能訪問操作對象類型、創建及初始化對象、調用對象上的虛方法,直接操作數組元素,甚至能拋出和捕捉異常處理,可以說是一種面向對象的機器語言。
什么是JIT?它是如何工作的?
CLR執行托管模塊,必須要將IL代碼編譯成本地CPU指令。這個事情由JIT(just in time)來執行。下面將以一個簡單的例子來詳細闡述JIT是如何工作的:
static void Main() { Console.WriteLine("Hello"); Console.WriteLine("Goodbye"); }
1、CLR在執行Main函數檢查引用的所有類型,這里Main用到了Console類,CRL會為此分配一個內部結構。在這個結構中,Console類的所有方法都有對應的入口。
2、Mian首次調用WriteLine函數時,JITCCompiler函數會被調用。JITCCompiler知道是Console類定義了WriteLine函數,並在定義了Console類的程序集中查找WriteLine的IL代碼。
3、動態分配一塊內存;
4、對找到的IL代碼進行驗證,然后編譯成本地CPU指令,將該指令放到前面動態分配的內存中;
5、修改內部結構中WriteLine函數的入口,使其指向動態分配的內存的地址;
6、JITCCompiler函數跳轉到存有本地CPU指令;
7、執行完后,調回Main函數,繼續執行后面的代碼;
這里需要說明的是,第二次執行WriteLine函數的時候,並沒有JIT啥事情了,因為會直接調用內存塊中的本地CPU指令。
參考:《分清“語言/規范”以及“平台/實現”,以及跨平台.NET開發》
《CLR.via.C#》