.Net Discovery系列文章阅读索引--带你探索未知的.Net世界
? .Net Discovery系列文章是講述.Net平臺(tái)機(jī)制的文章,目前已有12篇,分別講述了.Net垃圾收集、實(shí)時(shí)編譯、字符串等部件的機(jī)制,現(xiàn)在推出1周年之際總結(jié)文章閱讀索引,希望對(duì)大家有所幫助。
?
? string--.Net平臺(tái)永恒的話題。這是一種比較特殊的數(shù)據(jù)類型,它是基元類型,也是引用類型,在編譯以及運(yùn)行時(shí),.Net平臺(tái)都對(duì)它做了一些優(yōu)化工作,正是這些優(yōu)化工作有時(shí)會(huì)迷惑編程人員,使string看起來難以琢磨,這篇文章分上下兩章,共四節(jié),來講講關(guān)于string的陌生一面。
| ? 重點(diǎn)回顧:? ? 在C#中,如果用new關(guān)鍵字實(shí)例化一個(gè)類,對(duì)應(yīng)是由IL指令newobj來完成的;而創(chuàng)建一個(gè)字符串,則由ldstr指令完成,看到ldstr指令,我們即可認(rèn)為,IL希望創(chuàng)建一個(gè)新的字符串 。 ? 從某些方面講,正是字符串的恒定性,才造就了字符串的駐留機(jī)制,也為字符串的線程同步工作大開方便之門(同一個(gè)字符串對(duì)象可以在不同的應(yīng)用程序域中被訪問,所以駐留的字符串是進(jìn)程級(jí)的,垃圾回收不能釋放這些字符串對(duì)象,只有進(jìn)程結(jié)束這些對(duì)象才被釋放)。 ? 在.Net中處理字符串時(shí),有一個(gè)很重要的機(jī)制,叫做字符串駐留機(jī)制。由于string是編程中用到的頻率較高的一種類型,CLR對(duì)相同的字符串,只分配一次內(nèi)存。CLR內(nèi)部維護(hù)著一塊特殊的數(shù)據(jù)結(jié)構(gòu),我們叫它字符串池,可以把它理解成是一個(gè)HashTable,這個(gè)HashTable維護(hù)著程序中用到的一部分字符串,HashTable的Key是字符串的值,而Value則是字符串的內(nèi)存地址。一般情況下,程序中如果創(chuàng)建一個(gè)string類型的變量,CLR會(huì)首先在HashTable遍歷具有相同Hash Code的字符串,如果找到,則直接把該字符串的地址返回給相應(yīng)的變量,如果沒有才會(huì)在內(nèi)存中新建一個(gè)字符串對(duì)象。 |
?
? 通過上一篇文章,大家會(huì)了解字符串駐留機(jī)制、恒定性、常量池等特性,這篇文章通過10個(gè)例子,來為大家講解string,同時(shí)如果你自己對(duì)string的了解程度,有足夠的信心,那么就來讀一下這篇文章,試試做一下10個(gè)例子,檢測一下自己有多“?!?。
| ? 重點(diǎn)回顧: ? 代碼九: ? ? ? ??? string a = "a"; ??? string b = new string('a', 1); ??? Response.Write(a.Equals(string.Intern(b))); ??? Response.Write(ReferenceEquals(a, string.Intern(b))); ? 輸出:True (Equals比較值,無論是否Intern都會(huì)相同) ? True (ReferenceEquals比較字符串對(duì)象的引用,Intern已經(jīng)將b駐留至字符串池內(nèi)) ? 代碼十: ??? string a = "str"; ??? string b = "str_2".Substring(0,3); ??? Response.Write(a.Equals(b)); ??? Response.Write(ReferenceEquals(a, b)); ? 輸出:True (Equals比較值,a與c的值相同) ? False (ReferenceEquals比較字符串對(duì)象的引用,Substring操作產(chǎn)生了新的字符串對(duì)象) ? 此段代碼產(chǎn)生了3個(gè)string對(duì)象,是哪3個(gè)呢?如果你不明白,還是從頭再看一遍吧! |
?
? 這篇文章將全面的為大家介紹.Net 垃圾收集的運(yùn)行方式、算法,以及與垃圾收集相關(guān)的關(guān)鍵方法。 說到垃圾收集機(jī)制,很少有人知道,垃圾收集并不是伴隨Java出現(xiàn)的,早在1958年,圖林獎(jiǎng)得主John發(fā)明的Lisp語言就已經(jīng)提供了GC的功能,這是GC的第一次出現(xiàn),是思想的一次閃光!而后,1984年Dave Ungar發(fā)明的Small talk語言第一次正式采用了GC機(jī)制。
? 這篇文章將重點(diǎn)為大家介紹.Net垃圾收集器、代齡、策略引擎,并結(jié)合Windbg為大家展現(xiàn)一個(gè)有趣的機(jī)制平臺(tái)。
| ? 重點(diǎn)回顧: ? .Net中采用了一種叫做“標(biāo)記與清除(Mark Sweep)”算法來完成上述任務(wù)。 “標(biāo)記與清除”算法,顧名思義,這種算法有兩個(gè)本領(lǐng): ? “標(biāo)記”本領(lǐng)——垃圾的識(shí)別:從應(yīng)用程序的root出發(fā),利用相互引用關(guān)系,遍歷其在Heap上動(dòng)態(tài)分配的所有對(duì)象,沒有被引用的對(duì)象不被標(biāo)記,即成為垃圾;存活的對(duì)象被標(biāo)記,即維護(hù)成了一張“根-對(duì)象可達(dá)圖”。 ? 其實(shí),CLR會(huì)把對(duì)象關(guān)系看做“樹圖”,無疑,了解數(shù)據(jù)結(jié)構(gòu)的同學(xué)都知道,有了“樹圖”的概念,會(huì)加快遍歷對(duì)象的速度。 ? 檢測、標(biāo)記對(duì)象引用,是一件很有意思的事情,有很多方法可以做到,但是只有一種是效率最優(yōu)的,.Net中是利用棧來完成的,在不斷的入棧與出棧中完成檢測:先在樹圖中選擇一個(gè)需要檢測的對(duì)象,將該對(duì)象的所有引用壓棧,如此反復(fù)直到棧變空為止。棧變空意味著已經(jīng)遍歷了這個(gè)局部根(或者說是樹圖中的節(jié)點(diǎn))能夠到達(dá)的所有對(duì)象。樹圖節(jié)點(diǎn)范圍包括局部變量(實(shí)際上局部變量會(huì)很快被回收,因?yàn)樗淖饔糜蚝苊黠@、很好控制)、寄存器、靜態(tài)變量,這些元素都要重復(fù)這個(gè)操作。一旦完成,便逐個(gè)對(duì)象地檢查內(nèi)存,沒有標(biāo)記的對(duì)象變成了垃圾。 ? “清除”本領(lǐng)——回收內(nèi)存:啟用Compact算法,對(duì)內(nèi)存中存活的對(duì)象進(jìn)行移動(dòng),修改它們的指針,使之在內(nèi)存中連續(xù),這樣空閑的內(nèi)存也就連續(xù)了,這就解決了內(nèi)存碎片問題,當(dāng)再次為新對(duì)象分配內(nèi)存時(shí),CLR不必在充滿碎片的內(nèi)存中尋找適合新對(duì)象的內(nèi)存空間,所以分配速度會(huì)大大提高。但是大對(duì)象(large object heap)除外,GC不會(huì)移動(dòng)一個(gè)內(nèi)存中巨無霸,因?yàn)樗垃F(xiàn)在的CPU不便宜。通常,大對(duì)象具有很長的生存期,當(dāng)一個(gè)大對(duì)象在.NET托管堆中產(chǎn)生時(shí),它被分配在堆的一個(gè)特殊部分中,移動(dòng)大對(duì)象所帶來的開銷超過了整理這部分堆所能提高的性能。 |
? 這一篇主要講了GC相關(guān)的重要方法。主要包括終止隊(duì)列(Finalization Queue)與可達(dá)隊(duì)列(Freachable Queue)、復(fù)生(Resurrection)、弱引用(WeakReference)、策略引擎、Dispose()、GC.Collect()、析構(gòu)函數(shù)(Finalize()等知識(shí)點(diǎn)。
| ? 重點(diǎn)回顧: ? 首先要了解與Finalize相關(guān)的兩個(gè)隊(duì)列:終止隊(duì)列(Finalization Queue)與可達(dá)隊(duì)列(Freachable Queue),這兩個(gè)隊(duì)列存儲(chǔ)了一組指向?qū)ο蟮闹羔槨?/span> ? 當(dāng)程序中在托管堆上分配空間時(shí)(new),如果該類含有析構(gòu)函數(shù),GC將在Finalization Queue中添加一個(gè)指向該對(duì)象的指針。 ? 在GC首次運(yùn)行時(shí),會(huì)在已經(jīng)被確認(rèn)為垃圾的對(duì)象中遍歷,如果某個(gè)垃圾對(duì)象的指針被Finalization Queue包含,GC將這個(gè)對(duì)象從垃圾中分離出來,將它的指針儲(chǔ)存到Freachable Queue中,并在Finalization Queue刪除這個(gè)對(duì)象的指針記錄,這時(shí)該對(duì)象就不是垃圾了——這個(gè)過程被稱為是對(duì)象的復(fù)生(Resurrection)。當(dāng)Freachable Queue一旦被添加了指針之后,它就會(huì)去執(zhí)行對(duì)象的Finalize()方法,清除對(duì)象占用的資源。 ? 當(dāng)GC再次運(yùn)行時(shí),便會(huì)再次發(fā)現(xiàn)這個(gè)含有Finalize()方法的垃圾對(duì)象,但此時(shí)它在Finalization Queue中已經(jīng)沒有記錄了(GC首次運(yùn)行時(shí)刪掉了它的Finalization Queue記錄),那么這個(gè)對(duì)象就會(huì)被回收了。 |
? JIT--實(shí)時(shí)編譯機(jī)制是.Net平臺(tái)的又一亮點(diǎn),這個(gè)文章將分為上下兩節(jié),從運(yùn)行原理、機(jī)制等方面,結(jié)合WinDbg為大家詳細(xì)的講解JIT方面的知識(shí)。關(guān)鍵字:JIT MSIL 元數(shù)據(jù) 方法表 托管模塊 本地映像。
| ? 重點(diǎn)回顧: ? JIT是運(yùn)行時(shí)的一個(gè)重要職責(zé)模塊,它將IL轉(zhuǎn)換為本地CPU指令,從上圖可以看出,也許你不敢相信,即時(shí)編譯這個(gè)過程是在運(yùn)行時(shí)發(fā)生的,這會(huì)不會(huì)對(duì)性能產(chǎn)生影響呢?事實(shí)上答案是雖然是肯定的,但這種開銷物有所值: ? 1. JIT所造成的性能開銷并不顯著。 ? 2. JIT遵循計(jì)算機(jī)體系理論中兩個(gè)經(jīng)典理論:局部性原理與8020原則。局部性原理指出,程序總是趨向于使用最近使用過的數(shù)據(jù)和指令,這包括空間的和時(shí)間的,將局部性原理引申可以得出,程序總是趨向于使用最近使用過的數(shù)據(jù)和指令,以及這些正在使用的數(shù)據(jù)和指令臨近的數(shù)據(jù)和指令(憑印象寫的,但不曲解原意);而8020原則指出,系統(tǒng)大多數(shù)時(shí)間總是花費(fèi)80%的時(shí)間去執(zhí)行那20%的代碼。 ? 根據(jù)這兩個(gè)原則,JIT在運(yùn)行時(shí)會(huì)實(shí)時(shí)的向前、后優(yōu)化代碼,這樣的工作只有在運(yùn)行時(shí)才可以做到。 ? 3. JIT只編譯需要的那一段代碼,而不是全部,這樣節(jié)約了不必要的內(nèi)存開銷。 ? 4. JIT會(huì)根據(jù)運(yùn)行時(shí)環(huán)境,即時(shí)的優(yōu)化IL代碼,即同樣的IL代碼運(yùn)行在不同CPU上,JIT編譯出的本地代碼是不同的,這些不同代碼面向自己的CPU做出了優(yōu)化。 ? 5. JIT會(huì)對(duì)代碼的運(yùn)行情況進(jìn)行檢測,并對(duì)那些特殊的代碼經(jīng)行重新編譯,在運(yùn)行過程中不斷優(yōu)化。 |
? 這一篇文章主要講了JIT一些實(shí)例,結(jié)合Windbg,對(duì)代碼進(jìn)行運(yùn)行時(shí)監(jiān)控,并通過Windbg的反饋向大家展示運(yùn)行時(shí)編譯的過程。
| ? 重點(diǎn)回顧: ? 回車后注意高亮區(qū)域的信息:
? 圖8 JIT前A類型的信息 ? 高亮區(qū)域顯示的是“<not loaded yet>”,這說明雖然運(yùn)行和程序,但未點(diǎn)擊按鈕時(shí),A類型未被JIT,因?yàn)樗€沒有入口地址。這一點(diǎn)體現(xiàn)了即時(shí)、按需編譯的思想。 ? 同樣,!name2ee *!JITTester.B和!name2ee *!JITTester.C命令會(huì)得到同樣的結(jié)果。 ? 好,現(xiàn)在做第4步操作,Detach Debuggee進(jìn)程,并回到程序中點(diǎn)擊“GO”按鈕
圖9 點(diǎn)擊按鈕 ? 第五步 重新附加進(jìn)程(參考第一步),這時(shí)程序已經(jīng)調(diào)用了new A().a1()方法,并重新執(zhí)行命令!name2ee *!JITTester.A ,注意高亮部分
圖10 JIT后A類型的信息 ? 和圖8中的信息比較,圖10中的方法表地址已經(jīng)變?yōu)镴IT后的內(nèi)存地址,這時(shí)圖4中的Stub槽將被一條強(qiáng)制跳轉(zhuǎn)語句替換,跳轉(zhuǎn)目標(biāo)與該地址有關(guān)。這一點(diǎn)說明JIT在大多情況下,只編譯一次代碼。 |
??? 新年伊始,該文章是博客園2010年的第一篇文章,感興趣的同學(xué)可以注意一下該文章的發(fā)布日期,是2010年1月1日1秒。
??? 本文分三節(jié)為大家深入介紹.Net GC的完整收集(Full GC)機(jī)制 、GC工作模式以及.Net 4.0中GC的特性方法。
| ? 重點(diǎn)回顧: ? Workstation GC without Concurrent: 用于單CPU的服務(wù)器,策略引擎會(huì)調(diào)節(jié)GC工作頻率,使用掛起->查找與標(biāo)記->壓縮->恢復(fù)的流程進(jìn)行GC工作。 ? Workstation GC with Concurrent: Concurrent GC與Non Concurrent GC模式相比,有著更敏捷的反應(yīng)速度,Winform應(yīng)用程序和Windows services?? 服務(wù)程序默認(rèn)采用這種模式,單CPU機(jī)器上只能使用workstation GC方式,默認(rèn)為 Workstation GC with Concurrent。 在這種模式下,第0、1代的收集仍然是要暫時(shí)掛起應(yīng)用程序的,只有在收集第2代時(shí),才會(huì)并行處理,這種并行收集是利用多CPU 對(duì)Full GC進(jìn)行并行處理,具體原理是將Full GC過程切分成多個(gè)短暫子過程對(duì)線程進(jìn)行凍結(jié),在線程凍結(jié)時(shí)間之外,應(yīng)用程序仍然可 以正常運(yùn)行。這主要通過將0代空間設(shè)置的很大,使Full GC時(shí),CLR仍然能夠在0代中進(jìn)行內(nèi)存分配,如果Full GC時(shí)0代內(nèi)存也已用盡,那么應(yīng)用程序?qū)⒈粧炱?#xff0c;等待Full GC的完成。 ? Server GC: 用于多CPU的服務(wù)器,這種GC模式有著很高的性能和效率。這種模式下,CLR為每個(gè)CPU創(chuàng)建一個(gè)專用的GC線程,每個(gè)CPU可以獨(dú)立的為相應(yīng)的? heap執(zhí)行GC操作,這些GC線程是以非并發(fā)的形式工作的,收集工作與線程正常工作不能同時(shí)進(jìn)行,這就是說第0、1、2代的收集都會(huì)掛起應(yīng)用線程。 ? 在.Net 4.0中,有一種新的垃圾收集機(jī)制,叫做后臺(tái)收集。這種機(jī)制以concurrent GC為基礎(chǔ)的,如上文所講,Workstation GC with Concurrent模式中,在Full GC過程時(shí),CLR仍然能夠在0代中進(jìn)行內(nèi)存分配,如果Full GC時(shí)0代內(nèi)存也已用盡,那么應(yīng)用程序?qū)⒈粧炱?#xff0c;等待Full GC的完成。 這個(gè)過程在后臺(tái)收集機(jī)制中是這樣工作的,在進(jìn)行Full GC時(shí)可以同時(shí)進(jìn)行第0、1代收集,并且后臺(tái)收集是一個(gè)獨(dú)立線程完成的,這個(gè)進(jìn)程任務(wù)優(yōu)先級(jí)低于第0、1代收集,如果在后臺(tái)收集中需要對(duì)第0、1代收集,后臺(tái)收集將會(huì)等待第0、1代收集完成后再進(jìn)行工 作,當(dāng)然第0、1代收集是需要短暫掛起應(yīng)用的。 ? 后臺(tái)收集還會(huì)根據(jù)策略引擎的指示,動(dòng)態(tài)調(diào)節(jié)第0、1代的容量,減少前臺(tái)收集(第0、1代收集)次數(shù)。 |
? 本文是《.Net Discovery》系列文章(一)的勘誤版。
| 重點(diǎn)回顧: ? 所以,第三行C#代碼(a = "str_2";)的樣子看起來是在修改變量a的舊值"str_1",但實(shí)際上是創(chuàng)建了一個(gè)新的字符串"str_2",然后將變量a的指針指向了"str_2"的內(nèi)存地址,而"str_1"依然在內(nèi)存中沒有受到任何影響,所以變量b的值沒有任何改變---這就是string的恒定性,同學(xué)們,一定要牢記這一點(diǎn),在.Net中,string類型的對(duì)象一旦創(chuàng)建即不可修改!包括ToUpper、SubString、Trim等操作都會(huì)在內(nèi)存中產(chǎn)生新的字符串。 |
? 本文是《.Net Discovery》系列文章(二)的勘誤版。
| 重點(diǎn)回顧: ? 代碼二: ??? string a = "str_1str_2"; ??? string b = "str_1"; ??? string c = "str_2"; ??? string d = b + c; ??? Response.Write(a.Equals(d)); ??? Response.Write(ReferenceEquals(a, d)); ? 輸出:True(Equals比較字符串對(duì)象的值) ? False(ReferenceEquals比較字符串對(duì)象的引用,由于變量d的值為變量連接的結(jié)果,字符串駐留機(jī)制無效) ? 代碼三: ??? string b = "str_1" + "str_2"; ??? Response.Write(a.Equals(b)); ??? Response.Write(ReferenceEquals(a, b)); ? 輸出:True(Equals比較字符串對(duì)象的值) ? True (ReferenceEquals比較字符串對(duì)象的引用,由于變量b的值為常量連接的結(jié)果,字符串駐留機(jī)制有效。如果變量b的值由“常量+變量”的方式得出,則字符串駐留無效) |
? 轉(zhuǎn)眼間《.Net Discovery》系列文章已經(jīng)推出1年了,本文為該系列的第10-13篇文章,在本文中將對(duì)以前所講的.Net平臺(tái)知識(shí)做一個(gè)小小的總結(jié)與機(jī)制分析,引出并重點(diǎn)介紹這些機(jī)制對(duì)程序性能的影響與改進(jìn)建議。 本文將分為四部分,分別講述了:垃圾回收機(jī)制、即時(shí)編譯機(jī)制、異常處理機(jī)制、字符串駐駐留機(jī)制的原理與性能改進(jìn)建議。
? 本文主要介紹垃圾回收機(jī)制對(duì)系統(tǒng)性能的影響分析。
| 重點(diǎn)回顧: ? 垃圾收集器一般將托管堆中的對(duì)象分為3代,這可以通過調(diào)用GC.MaxGeneration得知,對(duì)象按照存在時(shí)間長短進(jìn)行分代,最短的分在第0代,最長的分在第2代,第2代中的對(duì)象往往是比較大的,第二代空間被稱作Large Object Heap,對(duì)于2代對(duì)象的回收,與第0、1代回收方式相比最大的不同在于,沒有了指針移動(dòng)的壓縮過程。 ? 如下圖,第一次GC時(shí),左邊第一列A-F表示內(nèi)存中的對(duì)象,位于淺藍(lán)色 區(qū)域,經(jīng)過Mark后,ACDF標(biāo)記為可用,Sweep過程清除了BE,Compact過程移動(dòng)了ACDF,使之位于連續(xù)存儲(chǔ)區(qū)域中;第二次使用綠色做標(biāo)記;第三次GC使用藍(lán)色表示標(biāo)記;可以看出第三次GC過程沒有了指針移動(dòng)的壓縮過程。
圖1 對(duì)象的回收 |
? 轉(zhuǎn)眼間《.Net Discovery》系列文章已經(jīng)推出1年了,本文為該系列的第10-13篇文章,在本文中將對(duì)以前所講的.Net平臺(tái)知識(shí)做一個(gè)小小的總結(jié)與機(jī)制分析,引出并重點(diǎn)介紹這些機(jī)制對(duì)程序性能的影響與改進(jìn)建議。 本文將分為四部分,分別講述了:垃圾回收機(jī)制、即時(shí)編譯機(jī)制、異常處理機(jī)制、字符串駐駐留機(jī)制的原理與性能改進(jìn)建議
? 本文主要介紹即時(shí)編譯機(jī)制對(duì)系統(tǒng)性能的影響分析。
| 重點(diǎn)回顧: ? 運(yùn)行時(shí),操作系統(tǒng)會(huì)根據(jù)托管模塊中各種頭信息,裝載相應(yīng)的運(yùn)行時(shí)框架,Load()被加載,由于是第一次加載,這會(huì)觸發(fā)對(duì)Load()的即時(shí)編譯,JIT會(huì)檢測Load()中引用的所有類型,并結(jié)合元數(shù)據(jù)遍歷這些類型中定義的所有方法實(shí)現(xiàn),并用一個(gè)特殊的HashTable(僅用于理解)儲(chǔ)存這些類型方法與其對(duì)應(yīng)的入口地址(在未被JIT前,這個(gè)入口地址為一個(gè)預(yù)編譯代理(PreJitStub),這個(gè)代理負(fù)責(zé)觸發(fā)JIT編譯),根據(jù)這些地址,就可以找到對(duì)應(yīng)的方法實(shí)現(xiàn)。 ? 在初始化時(shí),HashTable中各個(gè)方法指向的并不是對(duì)應(yīng)的內(nèi)存入口地址,而是一個(gè)JIT預(yù)編譯代理,這個(gè)函數(shù)負(fù)責(zé)將方法編譯為本地代碼。注意,這里JIT還沒有進(jìn)行編譯,只是建立了方法表!
圖2方法表、方法描述、預(yù)編譯代理關(guān)系 ? 圖2中所示的MS核心引擎指的是一個(gè)叫做MSCorEE的DLL,即Microsoft .NET Runtime Execution Engine,它是一個(gè)橋接DLL,連同mscorwks.dll主要完成以下工作: ? 1. 查找程序集中包含的對(duì)應(yīng)類型清單,并調(diào)用元數(shù)據(jù)遍歷出包含的方法。 ? 2. 結(jié)合元數(shù)據(jù)獲得這個(gè)方法的IL。 ? 3. 分配內(nèi)存。 ? 4. 編譯IL為本地代碼,并保存在第3步所分配的內(nèi)存中。 ? 5. 將類型表(就是指上文中提到的HashTable)中方法地址修改為第3步所分配的內(nèi)存地址。 ? 6. 跳轉(zhuǎn)至本地代碼中執(zhí)行。 ? 所以隨著程序的運(yùn)行時(shí)間增加,越來越多的方法的IL被編譯為本地代碼,JIT的調(diào)用次數(shù)也會(huì)不斷減少。 |
? 轉(zhuǎn)眼間《.Net Discovery》系列文章已經(jīng)推出1年了,本文為該系列的第10-13篇文章,在本文中將對(duì)以前所講的.Net平臺(tái)知識(shí)做一個(gè)小小的總結(jié)與機(jī)制分析,引出并重點(diǎn)介紹這些機(jī)制對(duì)程序性能的影響與改進(jìn)建議。 本文將分為四部分,分別講述了:垃圾回收機(jī)制、即時(shí)編譯機(jī)制、異常處理機(jī)制、字符串駐駐留機(jī)制的原理與性能改進(jìn)建議
?
? 本文主要介紹異常處理機(jī)制、字符串駐駐留機(jī)制對(duì)系統(tǒng)性能的影響分析。
| 重點(diǎn)回顧: ? .Net 中基本的異常捕獲與處理機(jī)制是由try…catch…finally塊來完成的,它們分別完成了異常的監(jiān)測、捕獲與處理工作。一個(gè)try塊可以對(duì)應(yīng)零個(gè)或多個(gè)catch塊,可以對(duì)應(yīng)零個(gè)或一個(gè)finally塊。不過沒有catch的try似乎沒有什么意義,如果try對(duì)應(yīng)了多個(gè)catch,那么監(jiān)測到異常后,CLR會(huì)自上而下搜索catch塊的代碼,并通過異常過濾器篩選對(duì)應(yīng)的異常,如果沒有找到,那么CLR將沿著調(diào)用堆棧,向更高層搜索匹配的異常,如果已到堆棧頂部依然沒有找到對(duì)應(yīng)的異常,就會(huì)拋出未處理的異常了,這時(shí)catch塊中的代碼并不會(huì)被執(zhí)行。所以距離try最近的catch塊將最先被遍歷到。 ? 最后三行的每一個(gè)Item被稱作Exception Handing Clause,EHC組成Exception Handing Table,EHT與正常代碼之間由ret返回指令隔開。 ? 可以看出,FormatException排列在EHT的第一位。 ? 當(dāng)代碼成功執(zhí)行或反之而返回后,CLR會(huì)遍歷EHT: ? 1. 如果拋出異常, CLR會(huì)根據(jù)拋出異常的代碼的“地址”找到對(duì)應(yīng)的EHC(IL_0001 to IL_0010為檢測代碼的范圍),這個(gè)例子中CLR將找到2條EHC, FormatException會(huì)最先被遍歷到,且為適合的EHC。 ? 2. 如果返回的代碼地址在IL_0001 to IL_0029內(nèi),那么還會(huì)執(zhí)行finally handler 即IL_0029 to IL_0033中的代碼,不管是否因成功執(zhí)行代碼而返回。 ? 事實(shí)上,catch與finally的遍歷工作是分開進(jìn)行的,如上文所言,CLR首先做的是遍歷catch,當(dāng)找到合適的catch塊后,再遍歷與之對(duì)應(yīng)finally;而且這個(gè)過程會(huì)遞歸進(jìn)行至少兩次,因?yàn)榫幾g器將C#的try…catch…finally翻譯成IL中的兩層嵌套。 ? 當(dāng)然如果沒有找到對(duì)應(yīng)的catch塊,那么CLR會(huì)直接執(zhí)行finally,然后立即中斷所有線程。Finally塊中的代碼肯定會(huì)被執(zhí)行,無論try是否檢測到了異常。 |
總結(jié)
以上是生活随笔為你收集整理的.Net Discovery系列文章阅读索引--带你探索未知的.Net世界的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Intel Developer Foru
- 下一篇: Flash cs5 初试