簡(jiǎn)介:
這篇文章假定你熟悉我上兩篇文章中(GC101, GC102)提到的”Dispose/Finalize”模式。
微軟引入了析構(gòu)模式(pattern of finalization),目的是想使編碼更安全。如果一個(gè)開(kāi)發(fā)者引用了一個(gè)對(duì)象(Component)的實(shí)例而忘記銷(xiāo)毀它(通過(guò)調(diào)用Dispose方法),此組件仍然能被GC自動(dòng)回收。
讓我來(lái)講解一些實(shí)現(xiàn)析構(gòu)函數(shù)的負(fù)面效應(yīng)(譯注:即對(duì)性能產(chǎn)生負(fù)面影響的因素):(對(duì)那些沒(méi)有實(shí)現(xiàn)析構(gòu)函數(shù)的對(duì)象來(lái)說(shuō),以下是不存在的):
1. 對(duì)象被放入“析構(gòu)隊(duì)列”(finalization queue)時(shí)
2. 對(duì)象從”析構(gòu)隊(duì)列”中移走時(shí)
3. 對(duì)象被置入”將要被析構(gòu)隊(duì)列”(to be finalized queue)中時(shí)
4. 組件的析構(gòu)方法要被調(diào)用時(shí)
5. 對(duì)象從“將要被析構(gòu)的隊(duì)列”中移走時(shí)
6. 你無(wú)法準(zhǔn)確知道GC要調(diào)用析構(gòu)方法(進(jìn)行資源回收)的時(shí)間
這幾點(diǎn)概述了關(guān)于使用析構(gòu)方法時(shí)的影響。正如你所見(jiàn),析構(gòu)方法對(duì)性能有著巨大的沖擊。以下讓我們繼續(xù)深度探討這個(gè)問(wèn)題:
一個(gè)析構(gòu)線程
以上幾點(diǎn)中對(duì)性能影響最大的就是第4點(diǎn)。.NET的垃圾回收機(jī)制有一個(gè)專(zhuān)門(mén)從“to be finalized queue”調(diào)用其析構(gòu)函數(shù)的工作線程。這個(gè)線程以高優(yōu)先級(jí)運(yùn)行,因此如果有很多組件需要釋放資源(析構(gòu)),這個(gè)工作線程就會(huì)阻塞它們以低優(yōu)先級(jí)執(zhí)行。如果有很多垃圾要回收,你的進(jìn)程就會(huì)受到拒絕服務(wù)攻擊(DOS)!
入隊(duì)/出隊(duì)
以上提到入隊(duì)/出隊(duì)會(huì)對(duì)性能造成沖擊。對(duì)簡(jiǎn)單的對(duì)象來(lái)說(shuō),這是可以接受的。但如果想精確控制它對(duì)性能造成的影響,那么就要手工清除了。
你無(wú)法控制GC何時(shí)執(zhí)行Finalize()。你只知道它何時(shí)應(yīng)該清理垃圾:當(dāng)資源不再需要,并且清理垃圾所造成的影響是可以接受的。通過(guò)在Dispose()或Close()方法中寫(xiě)入代碼,你就能在最洽當(dāng)?shù)臅r(shí)機(jī)進(jìn)行手工清理資源。
不要引用任何對(duì)象
你不能在Finalize()中引用任何已命名的對(duì)象。這是因?yàn)閷?duì)象回收是以不可預(yù)知的次序執(zhí)行的,因此你不知道所引用的對(duì)象是否之前已被回收掉。這會(huì)令你在實(shí)現(xiàn)析構(gòu)時(shí)受到某種限制。
降級(jí)的垃圾回收(Overall degraded garbage collection)
GC需要經(jīng)歷幾個(gè)周期才能將析構(gòu)對(duì)象回收。這種影響比它當(dāng)初看起來(lái)的要大得多,不僅會(huì)使你的對(duì)象存活更久,而且對(duì)那些它引用的對(duì)象也會(huì)如此。
當(dāng)GC將對(duì)象置于”to be finalized queue”中時(shí),它會(huì)將GC升級(jí)到第1級(jí)。第1級(jí)中的對(duì)象比第0代的對(duì)象更少機(jī)會(huì)被清理,這樣你的對(duì)象所引用的不再 需要的托管/非托管資源就會(huì)長(zhǎng)時(shí)間駐留在內(nèi)存中。
為什么需要實(shí)現(xiàn)析構(gòu)函數(shù)?
是否有什么動(dòng)機(jī)在里面呢?當(dāng)然啦,這是“確保”清理你的對(duì)象占用的資源。這是唯一需要實(shí)現(xiàn)析構(gòu)的原因。如果你的組件使用了資源,它們就要在使用后釋放。如果開(kāi)發(fā)者顯式地調(diào)用了對(duì)象的Dispose()方法(假定你在Dispose中調(diào)用了GC.SuppressFinalize),那么此資源就會(huì)被清理掉,而且你不必再擔(dān)心它了!如果一個(gè)開(kāi)發(fā)者忘記調(diào)用對(duì)象的Dispose方法,那么GC線程會(huì)在調(diào)用析構(gòu)函數(shù)時(shí)自動(dòng)清理資源。但是執(zhí)行析構(gòu)的時(shí)間是隨機(jī)的,這就不由得你控制了。
結(jié)論:
我看見(jiàn)路分兩條:
1. 把所有清理資源的代理放在Dispose(或Close)方法中,開(kāi)發(fā)者負(fù)責(zé)所有的對(duì)象清理的工作。如果你的對(duì)象使用的是非托管資源,這是合理的,因?yàn)槿绻阃浨謇硭鼈儯蜁?huì)造成內(nèi)存泄漏。這還會(huì)令開(kāi)發(fā)者按需要隨時(shí)完成清理工作。
2. 實(shí)現(xiàn)析構(gòu)函數(shù),而且只清理非托管資源。在Dispose方法中清理所有的托管資源。
受到最廣泛接受的是第2種,但我有些不同,讓我來(lái)解釋為什么:
如果開(kāi)發(fā)者調(diào)用對(duì)象的Dispose方法失敗時(shí),在測(cè)量時(shí)就會(huì)造成內(nèi)存泄漏(如果沒(méi)有出,那么測(cè)試工作就需要重新修正!)在發(fā)現(xiàn)一個(gè)泄漏后,可以在此處調(diào)用對(duì)象Dispose方法。然后就出產(chǎn)生最佳性能。
在一個(gè)framework類(lèi)中,實(shí)現(xiàn)析構(gòu)的方法有很多種。我會(huì)在以后發(fā)表出來(lái),請(qǐng)留意我的blog。
|
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!