CLR的首要目標(biāo)
到目前我們已經(jīng)對CLR有了初步的了解津畸,對幫助了解CLR要解決的問題非常有用羞福。從很高的層次上講,CLR只有一個目標(biāo):
CLR的目標(biāo)是使編程變得更簡單绎狭。
有兩個因素使得這個陳述很有用。首先褥傍,這是CLR后續(xù)升級中 非常 有用的指導(dǎo)原則儡嘶。如,只有簡單的東西才容易用恍风,因此在CLR中添加一些 用戶可見 的復(fù)雜度是有疑慮的蹦狂。對添加一個新功能來說,比投資產(chǎn)出比更重要的是 復(fù)雜度/所有場景應(yīng)用收益權(quán)重 比朋贬。理想情況是這個比例是負的凯楔,即添加一個新功能可以移除某些限制或者將某些特殊場景泛化。但一般情況下是通過最小化復(fù)雜度和最大化新功能支持的使用場景來盡量降低這個比值锦募。
第二個原因是這個目標(biāo)之所以重要摆屯,是因為 CLR的成功基于易于使用。CLR并不是因為其比原生程序更快或者更锌纺丁(實際上虐骑,設(shè)計良好的原生程序經(jīng)常是贏家)而獲得成功,CLR也不是因為其包含某些特定功能而成功(如垃圾回收赎线,平臺無關(guān)你画,面向?qū)ο缶幊袒蛘咧С侄喟姹荆LR之所以成功是因為其整合這么多以及其他很多功能休讳,使得編程比其他平臺更簡單寿羞,一些非常重要但是經(jīng)常被忽視的功能如:
- 簡化的編程語言(如C#和Visual Basic比C++簡單不少)。
- 一套致力于易用使用的類庫(如矫废,我們只有一種字符串類型盏缤,而且它還是不可修改的,這個特性極大簡化了處理字符串的API)蓖扑。
- 嚴謹?shù)念悗烀?guī)范(如要求API都使用完整單詞并且統(tǒng)一命名規(guī)范)唉铜。
- 強大的編程工具鏈(如果Visual Studio是的編寫CLR程序極其簡單,智能感知也極大方便編程時查找正確類型和函數(shù))律杠。
就是這個致力于簡單的做法成為CLR成功的重要因素潭流。奇怪的是,很多重要的易用使用的功能都是相當(dāng)乏味的柜去。如灰嫉,任何編程環(huán)境都要求統(tǒng)一命名規(guī)范,但是在一個很大的類庫里做到這一點需要大量的工作嗓奢。而且這個工作經(jīng)常與其他目標(biāo)沖突(如保持跟之前版本的接口的兼容性)讼撒,或者就是有很高的成本(如在一個 非常 龐大的代碼庫里重命名一個函數(shù)的成本)。也就是這個目標(biāo)時刻提醒我們并在工作中將其放在最優(yōu)先的位置。
CLR的重要功能
CLR有很多功能根盒,可以歸類如下:
基礎(chǔ)功能 - 是其他功能的設(shè)計的根基钳幅,包括:
a. 垃圾回收
b. 內(nèi)存安全和類型安全
c. 對編程語言的高階支持次要功能 - 基于基礎(chǔ)功能,但不是被很多程序用到
a. 進程隔離的應(yīng)用程序域(AppDomain)機制
b. 進程安全和沙盒環(huán)境其它功能 - 運行環(huán)境所需但是不基于基礎(chǔ)功能炎滞,它們是為了創(chuàng)建一個完整的編程環(huán)境而設(shè)計:
a. 多版本
b. 調(diào)試/進程剖析
c. 互操作
CLR里的垃圾回收
在所有CLR提供的功能里敢艰,垃圾回收值得特別說明。垃圾回收是自動內(nèi)存回收機制的普遍術(shù)語册赛。在垃圾回收系統(tǒng)里钠导,用戶進程不需要調(diào)用特定的操作來清空內(nèi)存。運行時負責(zé)在垃圾回收內(nèi)存堆里實時跟蹤所有引用森瘪,它會遍歷內(nèi)存找出仍被進程使用到的那些引用辈双。而其它內(nèi)存則被當(dāng)作 垃圾 并可以用來處理新的內(nèi)存分配請求。
垃圾回收是一個非常有用的功能柜砾,因為其簡化了編程湃望。顯而易見的簡化是不再需要顯式的刪除操作。雖然去掉刪除操作的確很重要痰驱,但其給程序員帶來其它更實質(zhì)的價值:
- 垃圾回收簡化了接口設(shè)計证芭,因為你不再需要仔細設(shè)計接口的那一方負責(zé)刪除接口傳遞的對象。如:CLR接口只簡簡單單的返回字符串担映,不附帶字符串的緩存和長度废士。這也意味著不需要擔(dān)心字符串緩存的長度是否過小。因此蝇完,垃圾回收使得運行時上的所有接口都比之前的簡單官硝。
- 垃圾回收消除了很多常見的錯誤。在處理一個對象的生命周期時很容易犯錯短蜕,要么就是銷毀太早了(導(dǎo)致內(nèi)存破壞)氢架,要么就是太遲了(導(dǎo)致內(nèi)存泄漏)。一般程序會用到百萬級別的對象朋魔,導(dǎo)致發(fā)生這種錯誤的概率很高岖研。而且,追蹤這樣的生命周期方面的bug非常難警检,特別是對于那些被很多對象引用到的對象孙援。消除這類內(nèi)存方面的錯誤避免了很多悲劇。
然而扇雕,不是因為垃圾回收以上有用的功能使得我們在這里特別說明它拓售。它對運行時的簡單要求使得其更重要:
垃圾回收要求所有指向GC堆的引用都是可跟蹤的。
雖然這是一個非常簡單的要求镶奉,其實它對運行時有著深遠的意義础淤。正如你想象的那樣崭放,在程序運行的任意時刻知道指向一個對象的每一個指針,是非常難的值骇。于是我們采取了折中的辦法莹菱,技術(shù)上來說移国,只要GC發(fā)生的時候才會滿足這個要求(因此吱瘩,在理論上將我們不需要隨時知道所有的GC引用,只需要在GC的時候知道即可)迹缀。在實際操作中使碾,就連這個折中的辦法都因為CLR的另外一個功能而無法完全滿足:
CLR支持在一個進程里運行多個并發(fā)線程。
在任意時刻祝懂,正在執(zhí)行的其它線程的分配請求會觸發(fā)一個GC票摇。而多個并發(fā)的線程的執(zhí)行順序是無法準(zhǔn)確獲知的。我們無法精準(zhǔn)獲知如果一個線程觸發(fā)了GC請求砚蓬,另外一個線程正在干什么矢门。因此,GC可以在任何時候觸發(fā)灰蛙。CLR不需要 立即 響應(yīng)其它線程的GC請求祟剔,CLR需要一點“回旋空間”來避免實時追蹤GC引用,但它需要在其它線程觸發(fā)GC請求時有足夠空間來“及時”響應(yīng)摩梧。
這意味著CLR 幾乎 需要實時追蹤 所有 指向GC堆的引用物延。由于GC引用也許存在于機器的寄存器上、在局部變量里仅父、靜態(tài)或者其他字段里叛薯,有很多地方需要跟蹤。最麻煩的地方是寄存器和局部變量笙纤,因為它們與用戶代碼的實際運行情況密切相關(guān)耗溜。事實上,這對操作GC引用的 機器代碼 有一個額外的要求:它必須跟蹤所有它用到的GC引用省容。這意味著編譯器需要做一些額外工作來產(chǎn)生跟蹤這些引用的指令强霎。
請查看文章 垃圾回收設(shè)計文檔 來了解更多信息。