規(guī)范不僅沒有提供終結(jié)器或是清理器會(huì)?刻運(yùn)?的保證,也沒有對(duì)其?定會(huì)運(yùn)?提供任何保證。完全有可能出現(xiàn)這樣的情況,當(dāng)程序終?時(shí),它并沒有對(duì)早就處于不可達(dá)的對(duì)象運(yùn)?其終結(jié)器和清理器。因此,你永遠(yuǎn)都不應(yīng)該依賴于終結(jié)器或是清理器來更新持久化狀態(tài)。?如說,依賴于終結(jié)器或是清理器來釋放如數(shù)據(jù)庫等共享資源上的持久化鎖可能會(huì)導(dǎo)致整個(gè)分布式系統(tǒng)陷?癱瘓狀態(tài)。
不要被?法System.gc與System.runFinalization所誘惑。他們可能會(huì)增加終結(jié)器或是清理器執(zhí)?的?率,不過并不保證這?點(diǎn)。曾經(jīng)有兩個(gè)?法做過這個(gè)保證:System.runFinalizersOnExit及其搭檔Runtime.runFinalizersOnExit。這兩個(gè)?法存在嚴(yán)重的問題,早就已經(jīng)不建議使?了[ThreadStop]。
終結(jié)器的另?個(gè)問題是在執(zhí)?終結(jié)時(shí),未捕獲的異常會(huì)被忽略掉,這時(shí)對(duì)象的終結(jié)會(huì)被終?[JLS, 12.6]。未捕獲的異常會(huì)導(dǎo)致其他對(duì)象的狀態(tài)被破壞掉。如果另外的線程使?這個(gè)被破壞的對(duì)象,那就會(huì)導(dǎo)致不確定的?為發(fā)?。正常情況下,未捕獲的異常會(huì)終?線程并打印堆棧信息,但如果在終結(jié)器中就不會(huì)這樣——它甚?不會(huì)打印出任何警告信息。清理器不存在這個(gè)問題,因?yàn)槭?了清理器的庫會(huì)??控制其線程。
使?終結(jié)器與清理器會(huì)導(dǎo)致嚴(yán)重的性能問題。在我的機(jī)器上,創(chuàng)建?個(gè)簡(jiǎn)單的AutoCloseable對(duì)象,使?try-with-resources關(guān)閉它,然后讓垃圾收集器對(duì)其進(jìn)?回收,?約需要花費(fèi)12ns。使?終結(jié)器會(huì)導(dǎo)致時(shí)間增加到550ns。換句話說,使?終結(jié)器來創(chuàng)建和銷毀對(duì)象的時(shí)間要慢50倍。這主要是因?yàn)榻K結(jié)器阻礙了?效的垃圾回收。如果使?清理器來清除類的所有實(shí)例(在我的機(jī)器上每個(gè)實(shí)例?約需要花費(fèi)500ns),它在速度上與終結(jié)器?致相同;不過,如果只是將清理器作為?個(gè)安全?(后續(xù)將會(huì)介紹),那么其速度將會(huì)快很多。在這些情況下,我的機(jī)器上創(chuàng)建、清理與銷毀?個(gè)對(duì)象所花費(fèi)的時(shí)間?約需要66ns,這意味著你為安全?的使?需要付出5倍因?(不是50倍)的代價(jià)。
終結(jié)器存在?個(gè)嚴(yán)重的安全問題:他們會(huì)使你的類遭遇到終結(jié)器攻擊。終結(jié)器攻擊背后的想法?常簡(jiǎn)單:如果異常是從構(gòu)造?法或是序列化?法readObject與readResolve中拋出的(第12章),那么惡意的?類終結(jié)器就會(huì)運(yùn)?在部分構(gòu)建完畢的對(duì)象上,?這個(gè)對(duì)象本應(yīng)該『中途夭折的』。這個(gè)終結(jié)器會(huì)將對(duì)對(duì)象的引?記錄在?個(gè)靜態(tài)字段中,防?其被垃圾回收掉。?旦將這個(gè)不完整的對(duì)象記錄下來后,我們就可以輕松調(diào)?這個(gè)對(duì)象上的任意?法,?這個(gè)對(duì)象原本是不應(yīng)該存在的。從構(gòu)造?法中拋出異常?以禁?對(duì)象的創(chuàng)建;但在使?終結(jié)器的情況下,卻并?如此。這種攻擊還會(huì)產(chǎn)??常嚴(yán)重的后果。終態(tài)類不受終結(jié)器攻擊的影響,因?yàn)闆]?可以創(chuàng)建終態(tài)類的惡意?類。若想保護(hù)?終態(tài)類免受終結(jié)器攻擊,請(qǐng)編寫?個(gè)什么都不做的final的finalize?法。
那么,對(duì)于封裝了需要終?的資源(如?件或是線程)的對(duì)象來說,如果不為類編寫終結(jié)器或是清理器,那該怎么辦呢?只需讓類實(shí)現(xiàn)AutoCloseable即可,并讓其客戶端在不需要其實(shí)例時(shí)調(diào)?其close?法,通常我們會(huì)使?try-with-resources來確保終?,即便在異常的情況下亦如此(條款9)。值得提及的?個(gè)細(xì)節(jié)是,實(shí)例必須要追蹤其是否已經(jīng)關(guān)閉了:close?法必須要在?個(gè)字段中記錄下對(duì)象已經(jīng)不再有效了,其他?法則必須要檢查該字段,如果當(dāng)對(duì)象已經(jīng)關(guān)閉后還調(diào)?這些?法,那就需要拋出IllegalStateException異常。