I want to fly higher
          programming Explorer
          posts - 114,comments - 263,trackbacks - 0
          第七條:避免使用終結(jié)方法
            
            1.終結(jié)方法finalizer通常是不可預(yù)測(cè)的,也是很危險(xiǎn)的.一般情況下是不必要的。使用終結(jié)方法會(huì)導(dǎo)致行為不穩(wěn)定,降低性能,以及可移植性問題。根據(jù)經(jīng)驗(yàn),應(yīng)避免使用終結(jié)方法。
            2.C++的程序員被告知不要把終結(jié)方法當(dāng)做是C++析構(gòu)器destuctors的對(duì)應(yīng)物。在C++中,析構(gòu)器是回收一個(gè)對(duì)象所占用資源的常規(guī)方法,是構(gòu)造器所必須的對(duì)應(yīng)物。在Java中,當(dāng)一個(gè)對(duì)象變得不可達(dá)到的時(shí)候,垃圾回收器會(huì)回收與該對(duì)象相關(guān)聯(lián)的存儲(chǔ)空間,并不需要程序員做專門的工作,C++的析構(gòu)器也可以用來回收其他的非內(nèi)存資源。而在Java中,一般用try-finally塊來完成類似的工作。
            3.終結(jié)方法的缺點(diǎn)在于不能保證會(huì)及時(shí)的執(zhí)行。從一個(gè)對(duì)象變得不可到達(dá)開始,到它的終結(jié)方法被執(zhí)行,所花費(fèi)的這段時(shí)間是任意長的。即注重時(shí)間time-critical的任務(wù)不應(yīng)該由終結(jié)方法來執(zhí)行。如用終結(jié)方法來關(guān)閉已經(jīng)打開的文件,這是嚴(yán)重錯(cuò)誤,因?yàn)榇蜷_文件的描述符是一種很有限的資源。由于JVM延遲執(zhí)行終結(jié)方法,所以大量的文件會(huì)保留在打開狀態(tài),當(dāng)一個(gè)程序不能再打開文件的時(shí)候,它可能會(huì)運(yùn)行失敗。
            4.及時(shí)的執(zhí)行終結(jié)方法時(shí)垃圾回收算法的一個(gè)主要功能,這種算法在不同的JVM實(shí)現(xiàn)中大相徑庭。如果程序依賴終結(jié)方法被執(zhí)行的時(shí)間點(diǎn),那么這個(gè)程序的行為在不同的JVM中運(yùn)行的表現(xiàn)可能會(huì)截然不同.如一個(gè)程序在你測(cè)試用的JVM平臺(tái)運(yùn)行的很好,而在你的最重要的顧客的JVM平臺(tái)上卻根本無法運(yùn)行, 這完全有可能。
            5.延遲終結(jié)過程并不只是一個(gè)理論問題。在很少見的情況下,為類提供終結(jié)方法,可能會(huì)隨意的延遲期實(shí)例的回收過程。如可能在調(diào)試一個(gè)長期運(yùn)行的GUI應(yīng)用程序的時(shí)候,該應(yīng)用程序總是莫名的出現(xiàn)OutOfMemoryError錯(cuò)誤而死掉。分析表明,該應(yīng)用程序死掉的時(shí)候,其終結(jié)方法隊(duì)列中有數(shù)千個(gè)圖形對(duì)象正在等待被終結(jié)和回收。遺憾的時(shí)候,終結(jié)方法線程的優(yōu)先級(jí)比該應(yīng)用程序的其他線程的要低很多,所以圖形對(duì)象的終結(jié)速度達(dá)不到他們進(jìn)入隊(duì)列的速度。Java語言規(guī)范并不保證哪個(gè)線程將會(huì)執(zhí)行終結(jié)方法,所以除了不使用終結(jié)方法之外,并沒有很輕便的方法能夠避免這樣的問題。
            6.Java語言規(guī)范不僅不能保證終結(jié)方法會(huì)被及時(shí)的執(zhí)行,而且根本就不保證他們會(huì)執(zhí)行。當(dāng)一個(gè)程序終止的時(shí)候,某些已經(jīng)無法訪問的對(duì)象上的終結(jié)方法卻根本沒有被執(zhí)行,這是完全可能的。結(jié)論是:不應(yīng)該依賴終結(jié)方法來更新重要的持久狀態(tài)。如依賴終結(jié)方法來釋放共享資源如數(shù)據(jù)庫上的永久鎖,很容易讓這個(gè)分布式系統(tǒng)垮掉。
            7.不要被System.gcSystem.runFinalization這兩個(gè)方法所誘惑,他們確實(shí)增加了終結(jié)方法被執(zhí)行的機(jī)會(huì),但是他們并不保證終結(jié)方法一定會(huì)被執(zhí)行。唯一聲稱保證終結(jié)方法被執(zhí)行的方法是System.runFinalizersOnExit以及其臭名昭著的孿生兄弟Runtime.runFinalizersOnExit,這兩個(gè)方法都有致命的缺陷,已經(jīng)被廢棄了。
             :System#runFinalizersOnExit|Runtime#runFinalizersOnExit:該方法具有固有的不安全性。它可能對(duì)正在使用的對(duì)象調(diào)用終結(jié)方法,而其他線程同時(shí)正在操作這些對(duì)象,從而導(dǎo)致不正確的行為或死鎖。
            8.當(dāng)你并不確定是否應(yīng)該避免使用終結(jié)方法的時(shí)候,這還有一種需要考慮的情況:如果未被捕獲的異常在終結(jié)過程中被拋出來了,那么這種異常可以被忽略,并且該對(duì)象的終結(jié)過程也會(huì)被終止。未被捕獲的異常會(huì)使對(duì)象處于破壞的狀態(tài)a corrupt state,如果另一個(gè)線程企圖使用這種被破壞的對(duì)象,則可能發(fā)生任何不確定的行為。 正常情況下,未被捕獲的異常會(huì)使線程終止,并打印出棧軌跡stack trace。但是如果異常發(fā)生在終結(jié)方法之中,則不會(huì)如此,甚至連警告都不會(huì)打印出來。
            9.還有一點(diǎn),使用終結(jié)方法有一個(gè)非常嚴(yán)重的Severe性能損失。某測(cè)試數(shù)據(jù):創(chuàng)建和銷毀一個(gè)簡單對(duì)象的時(shí)間大約為5.6ns。增加一個(gè)終結(jié)方法使時(shí)間增加到了2400ns。換句話說,用終結(jié)方法創(chuàng)建和銷毀對(duì)象慢了大約430倍。
            10.如果類的對(duì)象中封裝的資源如文件或者線程確實(shí)需要終止,應(yīng)該如何做而不編寫終結(jié)方法呢。只需提供一個(gè)顯示的終止方法,并要求該類的客戶端在每個(gè)實(shí)例不再有用的時(shí)候調(diào)用這個(gè)方法。有一個(gè)細(xì)節(jié)是該實(shí)例必須記錄下自己是否已經(jīng)被終止了;顯示的終止方法必須在一個(gè)私有域中記錄下“該對(duì)象已經(jīng)不再有效”。如果這些方法時(shí)在對(duì)象已經(jīng)終止之后被調(diào)用,其他的方法就必須檢查這個(gè)于,并拋出IllegalStateException。
            顯示終止方法的典型例子是InputStream,OutputStream,java.sql.Connection上的close方法。另一個(gè)例子是java.util.Timer上的cancel方法,它執(zhí)行必要的狀態(tài)改變,是的與Timer實(shí)例相關(guān)聯(lián)的該線程溫和的終止自己。java.awt中的例子還包括Graphics.dispose和Window.dispose這些方法通常由于性能不好而不被人們關(guān)注。一個(gè)相關(guān)的方法時(shí)Image.flush,它會(huì)釋放所有域Image實(shí)例相關(guān)資源,但是該實(shí)例仍然處于可用的狀態(tài),如果有必要的話,會(huì)重新分配資源.
            注:如1.FileOutputStream#private volatile boolean closed = false;
                   2.java.io.Closeable | java.lang.AutoCloseable[since 1.7]|
                {@link ExplicitClose}
             11.終結(jié)方法的好處:
               1.當(dāng)對(duì)象的所有者忘記調(diào)用之前建議的顯示終止方法時(shí),終結(jié)方法可以充當(dāng)安全網(wǎng),safety net.雖然這種做并不能保證終結(jié)方法會(huì)被及時(shí)的調(diào)用,但是在客戶端無法通過調(diào)用顯示的終止方法來正確結(jié)束操作的情況下(希望這種情形盡可能的少發(fā)生),遲一點(diǎn)釋放關(guān)鍵資源總比永遠(yuǎn)不釋放要好。但是如果終結(jié)方法發(fā)現(xiàn)資源還未被終止,則應(yīng)該在日志中記錄一條警告。因?yàn)檫@表示客戶端代碼中的一個(gè)bug,應(yīng)該得到修復(fù)。如果正考慮編寫這樣的安全網(wǎng)終結(jié)方法,就要考慮清楚這種額外的保護(hù)是否值得付出額外的代價(jià)。
                 2.顯示終止方法模式的實(shí)例中所示的四個(gè)類FileInputStream,FileOutputStream,Timer和Connection,都具有終結(jié)方法。當(dāng)他們的終止方法未能被調(diào)用的情況下,這些終結(jié)方法充當(dāng)了安全網(wǎng)。
                 注:FileInputStream#filnalize:
              /**
                  * Ensures that the <code>close</code> method of this file input stream is
                  * called when there are no more references to it.
                  *
                  * @exception  IOException  if an I/O error occurs.
                  * @see        java.io.FileInputStream#close()
                  */
                 protected void finalize() throws IOException {
                     if ((fd != null) &&  (fd != FileDescriptor.in)) {
             
                         /*
                          * Finalizer should not release the FileDescriptor if another
                          * stream is still using it. If the user directly invokes
                          * close() then the FileDescriptor is also released.
                          */
                         runningFinalize.set(Boolean.TRUE);
                         try {
                             close();
                         } finally {
                             runningFinalize.set(Boolean.FALSE);
                         }
                     }
                 }
             3.終結(jié)方法的第二種合理用于與對(duì)象的本地對(duì)等體native peer有關(guān)。本地對(duì)等體是一個(gè)本地對(duì)象native object,普通對(duì)象通過本地方法native method委托給一個(gè)本地對(duì)象。因?yàn)楸镜貙?duì)等體不是一個(gè)普通對(duì)象,所以垃圾回收器不會(huì)知道它。當(dāng)它的Java對(duì)等體被回收的時(shí)候,它不會(huì)被回收。在本地對(duì)等體并不擁有關(guān)鍵資源的前提下(注是否因?yàn)殛P(guān)鍵資源可被本地其他進(jìn)程使用),終結(jié)方法正是執(zhí)行任務(wù)的最合適的工具。如果本地對(duì)等體用于必須被及時(shí)終止的資源, 那么該類就應(yīng)該具有一個(gè)顯示的終止方法,如前所述。終止方法應(yīng)該完成所有必要的工作以便釋放關(guān)鍵的資源。終止方法可以使本地方法或者也可以調(diào)用本地方法。
            12.注意終結(jié)方法鏈finalizer chaining并不會(huì)自動(dòng)執(zhí)行。如果類不是Object有終結(jié)方法,并且子類覆蓋了終結(jié)方法,子類的終結(jié)方法就必須手動(dòng)調(diào)用超類的終結(jié)方法。你應(yīng)該在一個(gè)try快中終結(jié)子類,并且在相應(yīng)的finally塊中調(diào)用超類的終結(jié)方法。這樣可以保證,即使子類的終結(jié)過程拋出異常,超類的終結(jié)方法也會(huì)得到執(zhí)行。反之亦然。注:{@link FinalizerChaining}
            13.如果子類實(shí)現(xiàn)這覆蓋了超類的終結(jié)方法,但是忘了手工調(diào)用超類的終結(jié)方法或者有意選擇不調(diào)用超類的終結(jié)方法,那么超類的終結(jié)方法將永遠(yuǎn)不會(huì)得到調(diào)用。要防范這樣的粗心大意或者而已的子類是有可能的,代價(jià)就是為每個(gè)將被終結(jié)的對(duì)象創(chuàng)建一個(gè)附加的對(duì)象,不是把終結(jié)方法放在要求終結(jié)處理的類中,而是把終結(jié)方法放在一個(gè)匿名的類中,該匿名類的唯一用途是終結(jié)它的外圍實(shí)例enclosing instance.該匿名類的單個(gè)實(shí)例被稱為終結(jié)方法守衛(wèi)者finalizer guardian.外圍類的每個(gè)實(shí)例都會(huì)創(chuàng)建這樣一個(gè)守衛(wèi)者,外圍實(shí)例在它的私有實(shí)例域中保存一個(gè)對(duì)其終結(jié)方法守衛(wèi)者的唯一引用,因?yàn)榻K結(jié)方法守衛(wèi)者與外圍實(shí)例可以同時(shí)啟動(dòng)終結(jié)過程。當(dāng)守衛(wèi)者被終結(jié)的時(shí)候, 它執(zhí)行外圍實(shí)例所期望的終結(jié)行為,就好像它的終結(jié)方法是外圍對(duì)象上的一個(gè)方法一樣。
            注:{@link FinalizerGuardian}-->在終結(jié)方法守衛(wèi)者以為是實(shí)例變量?,所以必然會(huì)被銷毀?,進(jìn)而其終結(jié)的時(shí)候必然會(huì)調(diào)用外圍類的終結(jié)發(fā)方法。
            14.總結(jié):除非是作為安全網(wǎng)或者是為了終止非關(guān)鍵的本地資源,否則請(qǐng)不要使用終結(jié)方法。在這些很少見的情況下,既然使用了終結(jié)方法就要記住調(diào)用super.finalize.如果用終結(jié)方法作為安全網(wǎng),要記得記錄終結(jié)方法的非法用法。最好,如果需要把終結(jié)方法與公有的非final(注:final的無子類)的類關(guān)聯(lián)起來,請(qǐng)考慮使用終結(jié)方法守衛(wèi)者,以確保即使子類的終結(jié)方法未能調(diào)用super.finalize,該終結(jié)方法也會(huì)被執(zhí)行。

          部分源碼:
          package com.book.chap2.avoidFinalizer;

          /**
          *
          *顯示的終止方法
          *<pre>
          *1.通常與try-finally結(jié)構(gòu)結(jié)合起來使用,以確保及時(shí)終止。在finally子句內(nèi)部調(diào)用顯示的終止方法,可以保證即使在使用對(duì)象的時(shí)候有異常拋出,該終止方法
          *也會(huì)執(zhí)行。
          *</pre>
          *
          *@author landon
          *@since 1.6.0_35
          *@version 1.0.0 2013-3-14
          *
          */


          public class ExplicitClose
          {
             
          public void terminate()
             
          {
                 
          //顯示的終止方法
              }

             
             
          public static void main(Stringargs)
             
          {
                  ExplicitClose close = new ExplicitClose();
                 
          try
                 
          {
                     
          //做一些必要的事情
                  }

                 
          finally
                 
          {
                      close.terminate();//顯示的調(diào)用終止方法
                  }

              }

          }


          package com.book.chap2.avoidFinalizer;

          /**
          *
          *終結(jié)方法鏈不會(huì)自動(dòng)執(zhí)行,需手動(dòng)調(diào)用超類的終結(jié)方法。
          *<p>
          *    在一個(gè)try快中終結(jié)子類,并且在相應(yīng)的finally塊中調(diào)用超類的終結(jié)方法。這樣可以保證,即使子類的終結(jié)過程拋出異常,超類的終結(jié)方法也會(huì)得到執(zhí)行
          *</p>
          *
          *@author landon
          *@since 1.6.0_35
          *@version 1.0.0 2013-3-14
          *
          */


          public class FinalizerChaining
          {
              @Override
             
          protected void finalize() throws Throwable
             
          {
                 
          try
                 
          {
                     
          //終結(jié)子類的狀態(tài)
                  }

                 
          finally
                 
          {
                     
          super.finalize();
                  }

              }

          }


          package com.book.chap2.avoidFinalizer;

          /**
          *
          *終結(jié)方法保衛(wèi)者
          *<pre>
          *1.為每個(gè)將被終結(jié)的對(duì)象創(chuàng)建一個(gè)附加的對(duì)象,不是把終結(jié)方法放在要求終結(jié)處理的類中,而是把終結(jié)方法放在一個(gè)匿名的類中,
          *該匿名類的唯一用途是終結(jié)它的外圍實(shí)例enclosing instance.該匿名類的單個(gè)實(shí)例被稱為終結(jié)方法守衛(wèi)者finalizer guardian.
          *外圍類的每個(gè)實(shí)例都會(huì)創(chuàng)建這樣一個(gè)守衛(wèi)者,外圍實(shí)例在它的私有實(shí)例域中保存一個(gè)對(duì)其終結(jié)方法守衛(wèi)者的唯一引用,
          *因?yàn)榻K結(jié)方法守衛(wèi)者與外圍實(shí)例可以同時(shí)啟動(dòng)終結(jié)過程。當(dāng)守衛(wèi)者被終結(jié)的時(shí)候, 它執(zhí)行外圍實(shí)例所期望的終結(jié)行為,
          *就好像它的終結(jié)方法是外圍對(duì)象上的一個(gè)方法一樣
          *2.FinalizerGuardian類并沒有終結(jié)方法除了從Object繼承了一個(gè)無關(guān)緊要的之外,所以子類的終結(jié)方法是否調(diào)用super.finalizer并不重要.對(duì)于每一個(gè)帶有終結(jié)方法
          *的非final公有類(注意因?yàn)閒inal類無法被繼承),都應(yīng)該考慮使用這種方法。
          *</pre>
          *
          *@author landon
          *@since 1.6.0_35
          *@version 1.0.0 2013-3-14
          *
          */


          public class FinalizerGuardian
          {
             
          private final Object finalizerGuardian = new Object()
             
          {
                 
          protected void finalize() throws Throwable
                 
          {
                     
          //終結(jié)外圍對(duì)象
                  }

              }
          ;
          }


          posted on 2013-03-15 19:24 landon 閱讀(2166) 評(píng)論(0)  編輯  收藏 所屬分類: ProgramBook
          主站蜘蛛池模板: 沙洋县| 清原| 涿州市| 周宁县| 卫辉市| 嘉义市| 黄冈市| 蕉岭县| 贵阳市| 镇巴县| 南宫市| 元氏县| 新晃| 眉山市| 沙田区| 台湾省| 安化县| 孟州市| 淮北市| 赞皇县| 施甸县| 临颍县| 屏东市| 南充市| 麻城市| 平罗县| 吉安县| 平凉市| 内江市| 大安市| 玉林市| 武威市| 专栏| 莱阳市| 兴仁县| 奇台县| 曲麻莱县| 墨脱县| 清原| 庆城县| 康马县|