《CLR via C#》第四版
為什么有時(shí)候有JIT的語(yǔ)言會(huì)比直接編譯為機(jī)器碼的語(yǔ)言快?
簡(jiǎn)而言之,就是JIT所知道的信息比那些在開發(fā)機(jī)上直接編譯為機(jī)器碼的的編譯器知道的信息更多,有的時(shí)候這些信息是如此的有用,以至于效果可以超過(guò)JIT本身的開銷和JIT編譯時(shí)間受限帶來(lái)的限制。《CLR》給出了如下三種具體的原因:
1、 JIT知道當(dāng)前正在使用的CPU的特性,可以使用那些在當(dāng)前CPU上具備的新指令集。而傳統(tǒng)編譯器只能按照開發(fā)人員的指示,限制為僅使用較通用的指令集。更重要的是,這個(gè)過(guò)程是自動(dòng)的,對(duì)開發(fā)人員透明的。
2、 在執(zhí)行環(huán)境中,程序中有些變量可能已經(jīng)變?yōu)槌A浚绠?dāng)前CPU核心數(shù)、當(dāng)前進(jìn)程是否為64位進(jìn)程、進(jìn)程啟動(dòng)時(shí)的命令行參數(shù),這樣程序中依賴這些變量的分支/邏輯就可以被優(yōu)化。給JIT進(jìn)一步優(yōu)化最終代碼提供了操作空間。
3、 JIT并未只生成代碼一次,在程序運(yùn)行過(guò)程中,還可以監(jiān)視那些部分是程序的熱點(diǎn),即被大量反復(fù)執(zhí)行的代碼,對(duì)這些代碼可以根據(jù)PRofile的信息重新生成更為優(yōu)化的代碼。
除此以外,我認(rèn)為還有一個(gè)重要的原因是:
JIT可以跨越程序集文件的邊界來(lái)進(jìn)行優(yōu)化、內(nèi)聯(lián),這些程序集在編譯生成的時(shí)候可能是在不同的時(shí)間、不同的機(jī)器上,傳統(tǒng)編譯器對(duì)此無(wú)能為力。
checked 關(guān)鍵字的作用范圍僅在當(dāng)前所在函數(shù)內(nèi),不影響checked塊中調(diào)用的函數(shù),所以下面這段代碼不會(huì)拋異常
class Program{ static void Main(string[] args) { checked { Test(); } } private static void Test() { byte a = 100; a += 200; }}其實(shí)想想也正常,checked關(guān)鍵字的作用是將數(shù)值運(yùn)算編譯為帶檢查的IL指令,如果調(diào)用的函數(shù)在另一個(gè)程序集中,該程序集早已被編譯好,又如何改變呢?
但注意:Decimal并非基本類型,四則運(yùn)算沒有IL指令對(duì)應(yīng),所以不受checked影響,其運(yùn)算始終會(huì)拋異常。
值類型表示不會(huì)額外為此對(duì)象在對(duì)上分配,而值類型自己可能被包含在一個(gè)引用類型中,所以值類型未必不會(huì)在堆上。
值類型也用new關(guān)鍵字,容易給人造成誤解。
值類型可以通過(guò)顯示指定將多個(gè)值類型的字段重疊在一起。
只有C++/CLI才能獲得指向已裝箱的值類型的指針,C#只能先拆箱。
當(dāng)作為模板的類型參數(shù)時(shí),值類型會(huì)強(qiáng)制CLR為它專門生成一份特化的代碼,而只有引用類型的模板實(shí)例可以共享代碼,減少內(nèi)存占用。
個(gè)人經(jīng)驗(yàn):
引用類型new一次只有一個(gè)實(shí)例,而值類型則未必,當(dāng)值類型被傳遞和修改,其行為需要仔細(xì)分析各個(gè)值類型變量的生存期,給開發(fā)人員帶來(lái)不小的負(fù)擔(dān),這里面包括所謂的裝箱拆箱。建議只在局部范圍內(nèi)或是作為只讀對(duì)象的情況下才考慮使用,因?yàn)镃#碼農(nóng)普遍沒有C++碼農(nóng)那樣對(duì)對(duì)象生存期有明確的把握的能力,容易被豬隊(duì)友害死。
在GC回收時(shí),某個(gè)對(duì)象即使沒超出C++意義上的生存范圍(所在的塊),但由于在下面未運(yùn)行的代碼中沒有被引用所以一樣會(huì)被認(rèn)為沒有被引用而被GC。在/debug模式下,對(duì)象生存期會(huì)延長(zhǎng)到函數(shù)體結(jié)束。
using System; using System.Threading; public static class Program { public static void Main() { // Create a Timer object that knows to call our TimerCallback // method once every 2000 milliseconds. Timer t = new Timer(TimerCallback, null, 0, 2000); // Wait for the user to hit <Enter>. Console.ReadLine(); } private static void TimerCallback(Object o) { // Display the date/time when this method got called. Console.WriteLine("In TimerCallback: " + DateTime.Now); //Only once! // Force a garbage collection to occur for this demo. GC.Collect(); } }在進(jìn)程正常結(jié)束的時(shí)候CLR也會(huì)執(zhí)行GC過(guò)程,并釋放對(duì)象。
對(duì)于非托管的資源,建議使用SafeHandle系列管理其句柄,其基類CriticalFinalizerObject有如下CLR級(jí)別支持的額外特性:
1、 從CriticalFinalizerObject繼承的類型首次被引用時(shí),會(huì)JIT其析構(gòu)函數(shù),確保其在析構(gòu)時(shí)不會(huì)因內(nèi)存不足而失敗。
2、 在析構(gòu)時(shí),會(huì)優(yōu)先析構(gòu)其他不是從CriticalFinalizerObject繼承的對(duì)象,使得在普通類型的析構(gòu)函數(shù)中可以使用CriticalFinalizerObject類型的對(duì)象。
3、 當(dāng)整個(gè)AppDomain被強(qiáng)行卸載時(shí),CriticalFinalizerObject對(duì)象的析構(gòu)函數(shù)仍然會(huì)被調(diào)用。
此外:
1、 SafeHandle對(duì)象可以在P/Invoke時(shí)替代IntPtr作為參數(shù)和返回類型,確保異常安全。
2、 P/Invoke時(shí)會(huì)正確管理內(nèi)部的引用計(jì)數(shù),確保多線程引用的情況下不會(huì)被提前意外釋放。
3、 CriticalHandle是不帶引用計(jì)數(shù)的SafeHandle。
4、 SafeHandle和CriticalHandle及其子類都是抽象類,在具體場(chǎng)景需要通過(guò)繼承的方式使用。
dynamic類型被處理為Object+DynamicAttribute,所以不能通過(guò)Object和dynamic來(lái)實(shí)現(xiàn)不同的重載。
const的值會(huì)在編譯時(shí)被內(nèi)聯(lián),readonly則不會(huì),所以未來(lái)可能需要改動(dòng)的值不應(yīng)該用const。
Nullable類型在裝箱時(shí)CLR會(huì)特殊處理,脫掉Nullable,即null被裝箱為null,v被裝箱為v,而不是Nullable<V>類型。
可以通過(guò)AppDomain的FirstChanceException事件監(jiān)視異常被拋出,但事件回調(diào)函數(shù)不能處理這個(gè)異常。
如果一個(gè)異常沒有被CLR處理,被報(bào)告至Windows Error Reporting,那么它獲得的調(diào)用棧只能到最近一次被throw或是re-throw的位置,即re-throw對(duì)Windows Error Reporting無(wú)效,仍然會(huì)重置異常拋出點(diǎn)。
在Catch和Finally塊中,線程不會(huì)被Abort所中斷.
Environment.FailFast可以跳過(guò)普通的異常處理邏輯和對(duì)象Finalize方法直接結(jié)束進(jìn)程。
使用反射調(diào)用時(shí),如果拋出異常,會(huì)將該異常包裹為TargetInvocationException;dynamic不受此影響。
Constrained Execution Regions (CERs),該功能可以讓CLR預(yù)先在try塊之前“準(zhǔn)備”一段代碼,而不是在運(yùn)行過(guò)程中由于載入DLL失敗、類靜態(tài)初始化失敗等原因拋出異常。
Thread.Sleep(0)可以將CPU讓給同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線程,而Thread.Yield可以將CPU讓給更低優(yōu)先級(jí)的,介于Thread.Sleep(0)和Thread.Sleep(1)之間。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注