第二章 理論基礎(chǔ)-公用語言 運(yùn)行環(huán)境
既然你已經(jīng)具有了C#全面的印象,我也想讓你了解NGWS runtime的全貌。C#依靠由NGWS提供的運(yùn)行時;因此,有必要知道運(yùn)行時如何工作,以及它背后所蘊(yùn)含的概念。
所以,這一章分為兩部分——它們是所有的概念和使用的基礎(chǔ)。兩部分的內(nèi)容雖然有些重疊,但它有助于加深理解正在學(xué)習(xí)的概念。
2.1 NGWS Runtime
NGWS和NGWS Runtime為你提供了一種運(yùn)行時環(huán)境。該運(yùn)行時管理執(zhí)行代碼,并提供了使編程更容易的服務(wù)。只要你的編譯器支持這種運(yùn)行時,你就會從這種受管理的執(zhí)行環(huán)境中得益。
你猜測C#編譯器支持NGWS runtime很正確,但是不僅它支持NGWS runtime,VB和C++也支持。這些為支持運(yùn)行時所創(chuàng)建的代碼稱作"受管代碼"(managed code)。以下是你的應(yīng)用程序從NGWS runtime那里所得到的利益:
交叉語言集成(通過通用語言規(guī)范)
自動內(nèi)存管理(垃圾收集)
交叉語言異常處理(統(tǒng)一展開)
增強(qiáng)安全(包括類型安全)
版本支持("DLL地獄"終結(jié)者)
組件交互簡化模式
因NGWS runtime 要提供了所有的這些好處,編譯器必須把元文件和受管代碼一起發(fā)出。元文件描述代碼中的類型,它和你的代碼存在一起(與PE類似---PE為可變位執(zhí)行文件)
正如你從很多種交叉語言功能所看到的,NGWS runtime主要是關(guān)于高度集成交叉多異編程語言(tight integration
across multiple different programming
languages)。這種支持可達(dá)到允許你從一個VB對象派生出一個C#類的程度(我后面會給出要討論的文章)。
C#程序員將會喜歡的一個功能是,他們不必?fù)?dān)心內(nèi)存管理—也就是說不必?fù)?dān)心臭名昭著的內(nèi)存泄漏。NGWS runtime提供了內(nèi)存管理,當(dāng)對象和變量的生命期結(jié)束(不再被引用)時,垃圾收集器釋放它們。我真的喜歡這個功能,因?yàn)樵贑OM中的內(nèi)存管理一直是我的一塊心病。
應(yīng)該鼓勵配置一個管理應(yīng)用程序或者組件。因?yàn)楣芾響?yīng)用程序含有元數(shù)據(jù)文件,NGWS runtime可以利用這些信息,以確保你的應(yīng)用程序具有它所需的各種規(guī)定版本。所產(chǎn)生的明顯效果為,由于你的代碼沒有相互之間的依賴,很少可能出現(xiàn)中斷。
這章余下來的將分為兩部分,每一部分討論NGWS runtime的各個方面,直到你的C#應(yīng)用程序能執(zhí)行為止。
1、中間語言(Intermediate Language,縮寫IL)和元數(shù)據(jù)
2、即時編譯器(just-in-time compliers,簡稱JITers)
2.1.1 中間語言和元數(shù)據(jù)
由C#編譯器生成的受管代碼并不是原始代碼,但它是中間語言(IL)代碼。這種IL代碼自身變成了NGWS runtime的受管執(zhí)行進(jìn)程的入口。IL代碼明顯的優(yōu)勢在于它是CPU無關(guān)的,這也意味著,你要用目標(biāo)機(jī)器上的一個編譯器才能把IL代碼轉(zhuǎn)換成原始代碼。
盡管IL代碼由編譯器產(chǎn)生,但它并不是編譯器提供給運(yùn)行時僅有的東西。編譯器同樣產(chǎn)生有關(guān)你代碼的元數(shù)據(jù),它告訴運(yùn)行時有關(guān)你代碼的更多的東西,例
如各種類型的定義、各種類型成員的簽名以及其它數(shù)據(jù)。基本上,元數(shù)據(jù)是類型庫、注冊表內(nèi)容和其它用于COM的信息。盡管如此,元數(shù)據(jù)還是直接和執(zhí)行代碼合
并在一起,并不處在隔離的位置。
IL和元數(shù)據(jù)存放于擴(kuò)展了PE格式的文件中(PE格式用于.exe和.dll文件)。當(dāng)這樣的一個PE文件被裝載時,運(yùn)行時從文件中定位和分離出元數(shù)據(jù)和IL。
在進(jìn)一步說明之前,我想給你已有的IL指令的簡短目錄。盡管它不是一個完整的清單,也不需要你熟記和理解,但是它列出了你所必需的、C#程序所基于的知識基礎(chǔ)。
算術(shù)和邏輯操作符
控制流
直接內(nèi)存訪問
堆棧操作
參數(shù)和局部變量
堆棧分配
對象模式
實(shí)例類型值
臨界區(qū)
數(shù)組
分型位置
即時編譯器(JITters)
2.1.2 即時編譯器(JITters)
由C#或其它能產(chǎn)生受管代碼的編譯器所生成的受管代碼就是IL碼。雖然IL代碼被包裝在一個有效的PE文件中,但是你還是不能執(zhí)行它,除非它被轉(zhuǎn)換成為受管原始代碼。這就是NGWS runtime 即時編譯器(也稱作JITters)大顯身手的時候。
為什么你會對即時編譯代碼感到厭繁, 為什么不把整個IL PE文件編譯成原始代碼? 答案是時間——需要把IL代碼編譯成CPU規(guī)格的代碼的時間。這種編譯將更加有效率,因?yàn)橐恍┏绦蚨螐膩砭蜎]有被執(zhí)行過。例如,在我的字處理器中,郵件合并功能從來就沒有被編譯。
從技術(shù)上說,全部的處理過程如下:當(dāng)一個類型被裝載時,裝載器創(chuàng)建一個存根(stub),并使它連接每一個類型的方法。當(dāng)一個方法第一次被調(diào)用時,
存根把控制交給JIT。JIT把IL編譯為原始代碼,且把存根指針指向緩沖了的原始代碼。接著的調(diào)用將執(zhí)行原始碼。在某些位置上(At some
point),所有的IL都被轉(zhuǎn)換成為原始代碼,而JITter處于空閑狀態(tài)。
正如我在前面提到的,JIT編譯器有很多,不止一個。在Windows平臺上,NGWS runtime裝有3個不同的JIT編譯器。
JIT——這是NGWS runtime默認(rèn)使用的JIT編譯器。它是一個后臺(back end)優(yōu)化的編譯器 ,在前臺(up
front)實(shí)行數(shù)據(jù)流分析,并創(chuàng)建了高度優(yōu)化的受管原始代碼做為輸出結(jié)果。JIT可以使用不嚴(yán)格的IL指令集編碼,但是所需資源將十分可觀。主要的限制
在于內(nèi)存足跡(footprint)、結(jié)果工作集,以及實(shí)行優(yōu)化所消耗的時間。
EconoJIT——
和主JIT相比,EconJIT的目標(biāo)是把IL高速地轉(zhuǎn)換成受管原始代碼。它允許緩沖所產(chǎn)生的原始代碼,但是輸出碼并不象主JIT生成的代碼那樣優(yōu)化(代
碼小)。當(dāng)內(nèi)存緊張時,快速代碼生成方案的優(yōu)勢將蕩然無存。通過永久地拋棄無用的已JIT過的代碼,你可以把更大的IL程序裝入代碼緩沖區(qū)。因?yàn)镴IT編
譯快,執(zhí)行速度也仍然很快。
PreJIT—盡管它是基于主JIT的,但操作起來更象是一個傳 統(tǒng)的編譯器。你安裝了NGWS組件,它才能運(yùn)行,才可以把IL代碼編譯成受管原始代碼。當(dāng)然最終的結(jié)果為,更快的裝載時間和更快的應(yīng)用程序啟動時間(不需要更多的JIT編譯)。
在所列出的JITters中,有兩個是運(yùn)行時的JITters。可是你怎么決定要使用哪一個JIT,它如何使用內(nèi)存? 有一個稱做"JIT編譯管理器"的小應(yīng)用程序(jitman.exe),它存放于NGWS SDK安裝目錄下的bin目錄中。
盡管它是一個小小的對話框,可是你所選擇的選項(xiàng)功能是相當(dāng)強(qiáng)大的。每一個選項(xiàng)將在以下描述。
Use EconoJIT only 選項(xiàng)——當(dāng)該復(fù)選框沒有選上時,NGWS runtime使用默認(rèn)的正常的JIT編譯器。前面就曾經(jīng)解釋過兩種JITter的區(qū)別。
Max Code Pitch Overhead(%)選項(xiàng)——該設(shè)置僅保留給EconoJIT。它控制了JIT編譯時間和執(zhí)行代碼時間的百分比。如果超過了設(shè)定的域值,代碼緩沖區(qū)得到擴(kuò)充,以縮短JIT編譯所消耗的時間。
Limit Size of Code Cache選項(xiàng)——該項(xiàng)默認(rèn)為非選。沒有選擇該項(xiàng)意味著緩沖區(qū)將使用它所能得到的內(nèi)存。如果你想限制緩沖區(qū)大小,復(fù)選該選項(xiàng),這將允許你使用Max Size of Cache(bytes)選項(xiàng)。
Max Size of Cache(bytes)選項(xiàng)—控制容納JIT代碼的緩沖區(qū)的最大值。雖然你可以非常嚴(yán)格地限制這個值,但你還是應(yīng)該小心,不能超過這個緩沖區(qū)所適合的最大值。否則該方法的JIT編譯將會失敗。
Optimize For Size選項(xiàng)——告訴JIT 編譯器,優(yōu)化的目的是為了使代碼更小而不是能執(zhí)行得更快。這個設(shè)置默認(rèn)是關(guān)掉的。
Enable Concurrent GC[garbage collection]選
項(xiàng)——垃圾收集(GC)默認(rèn)地運(yùn)行在用戶代碼的線程中。意味GC發(fā)生時,可能會注意到回應(yīng)有輕微的延遲。為防止出現(xiàn)該現(xiàn)象,打開當(dāng)前GC。注意,當(dāng)前GC
比標(biāo)準(zhǔn)GC更慢,它僅在windows 2000上寫時(the time of writing)有效。
當(dāng)用C#創(chuàng)建項(xiàng)目時,你可能使用不同的設(shè)置試驗(yàn)過。當(dāng)創(chuàng)建 UI-intensive應(yīng)用程序時,你將會看到允許當(dāng)前GC的最大差別。
2.2 虛擬對象系統(tǒng)(VOS)
到目前為止,你僅看到了NGWS runtime如何工作,但是并不了解它工作的技術(shù)背景以及為什么它要這樣工作。這節(jié)都是關(guān)于 NGWS 虛擬對象系統(tǒng)的(VOS)。
以下為在VOS中形成聲明、使用和管理類型模型時,NGWS runtime的規(guī)則。在VOS背后的思想是建立一個框架,在執(zhí)行代碼時不能犧牲性能,允許交叉語言集成和類型安全。
我提到的框架是運(yùn)行時架構(gòu)的基礎(chǔ)。為了幫助你更好地了解它,我將它勾出四個區(qū)域。當(dāng)開發(fā)C#應(yīng)用程序和組件時,理解它們很重要。
VOS類型系統(tǒng)——提供豐富的類型系統(tǒng),它打算支持全面編程語言的完全實(shí)施。
元數(shù)據(jù)——描述和引用VOS類型系統(tǒng)所定義的類型。元數(shù)據(jù)的永久格式與編程語言無關(guān),但是,元數(shù)據(jù)拿自己當(dāng)作一種互換機(jī)制(nterchange mechanism)來使用,這種互換是在在工具和NGWS的虛擬執(zhí)行系統(tǒng)之間。