【轉】中間語言(IL)和即時編譯器(JIT)

     

2008-9-10 17:55:40   瘋狂代碼 http://CrazyCoder.cn/


        對於.NET的初學者來,一個很令人困惑的問題是:從高級語言(如C#和VisualBasic)到託管語言再到機器語言究竟是一個怎樣的過程。掌握這個過程也是理解.NET語言互操作性(也就是語言獨立性的核心原則)的關鍵,並且也關係到二進制兼容性的問題。儘管本書一直嘗試不探討這些的底層細節實現而主要集中講述如何最好地應用.NET,然而對CLR產生代碼過程有一個概覽對理解她的內部機理還是大有益處的。不僅如此,瞭解.NET產生代碼的地過程還可以幫助解決一些特殊的安全性問題。  
  
         .NET語言的編譯分爲兩個階段.首先高級語言被編譯成一種稱作IL的中間語言,與高級語言相比,IL更像是機器語言,然而,IL卻包含一些抽象概念(比如:類、異常),這也是這種語言被稱爲中間語言的原因。IL被打包在DLL或EXE文件中,而DLL和EXE在.NET中的主要區別就是:只有EXE可以直接被運行,而二者都可被某個正在執行的進程動態裝載(後文詳述)。由於機器的CPU只能執行本地彙編語言,而不是IL,進一步將IL編譯成彙編語言的工作(也就是第二階段)需要在運行時進行,這個過程由即時編譯器(JIT)完成。 
 
         高級語言在初次被編譯時,編譯器做兩件事:首先把編譯得到的IL存儲在DLL或EXE中,然後爲類的每個方法創建一個stub函數,此函數會調用即時編譯器,並將自身的地址作爲參數傳給編譯器。即時編譯器則從DLL或EXE中獲取相應的IL,編譯成機器語言,並將內存中的原零時調用函數替換成機器語言。這個過程的思想,是用已編譯的方法調用未編譯的方法,實質上被調用的是stub函數;stub函數再調用編譯器,將自身編譯爲本地機器語言;最後,.NET會重新調用該方法,方法此時才被真正地執行。函數被反覆調用時,機器指令會被直接執行,而只由編譯器對方法進行初次編譯需要花費時間。至於那些沒有被調用的方法,則不會被編譯。 
 
        當編譯器生成一個EXE文件後,該程序的入口函數爲Main()方法。裝載器將這個EXE文件載入,探測到該這是一個託管EXE,於是又載入.NET運行時庫文件(包括即時編譯器),接着調用了EXE的Main()方法。這將觸發對Main()方法的即時編譯,Main()方法在內存中被替換爲本地機器語言,於是.NET應用程序開始運行。在被編譯爲本地語言後,應用程序便可以自由調用本地代碼了。當程序中止時,本地代碼從內存中釋放,所以在下次運行時,IL需要被即時編譯器重新編譯。 
 
       你可能會對即時編譯對效率產生的影響而憂慮。然而這種憂慮應當減輕,因爲這種編譯方式比傳統的靜態代碼編譯要快。舉例來說,即使編譯器會區別特定類型的CPU(比如奔騰III和奔騰IV)並且根據CPU類型利用新增的指令集。相比較,傳統的編譯器產生代碼必須依賴於最低層次的標準,比如386指令集,因此不能利用較新型號的CPU的功能。以後版本的即時編譯器可能會被重寫,以適應應用程序的編碼方式(比如分支指令的使用頻率,分支預測等等),然後重新編譯代碼,以優化某個特殊應用程序(甚至是某個用戶!)對組件的使用方法。即時編譯器還可以根據實際機器資源(比如內存和CPU速度),對它自己生成的代碼進行優化。請注意,這些高級功能雖然目前還未實現,然而以後的機制很可能提供所有這些能力。總之,即時編譯器的優化過程,是通過增加編譯佔用的時間換取應用程序執行效率。這種優化的效果取決於程序的調用方式和用途。在未來,這種代價可以在應用程序的安裝過程中衡量,或者從用戶偏好庫中查找。                      


發佈了4 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章