理解finalize()-析構(gòu)函數(shù)的替代者

          by Tim Gooch

          在許多方面,Java 類似于 C++。Java 的語法非常類似于 C++,Java 有類、方法和數(shù)據(jù)成員;Java 的類有構(gòu)造函數(shù); Java 有異常處理。

          但是,如果你使用過 C++ 會(huì)發(fā)現(xiàn) Java 也丟掉一些可能是你熟悉的特性。這些特性之一就是析構(gòu)函數(shù)。取代使用析構(gòu)函數(shù),Java 支持finalize() 方法。

          在本文中,我們將描述 finalize() 與 C++ 析構(gòu)函數(shù)的區(qū)別。另外,我們將創(chuàng)建一個(gè)簡(jiǎn)單的 Applet 來演示 finalize() 是如何工作的。

          最終的界限

          與 Java 不同,C++ 支持局部對(duì)象(基于棧)和全局對(duì)象(基于堆)。因?yàn)檫@一雙重支持,C++ 也提供了自動(dòng)構(gòu)造和析構(gòu),這導(dǎo)致了對(duì)構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用,(對(duì)于堆對(duì)象)就是內(nèi)存的分配和釋放。

          在 Java 中,所有對(duì)象都駐留在堆內(nèi)存,因此局部對(duì)象就不存在。結(jié)果,Java 的設(shè)計(jì)者覺得不需要析構(gòu)函數(shù)(象 C++ 中所實(shí)現(xiàn)的)。

          取而代之,Java 定義了一個(gè)特殊的方法叫做finalize() ,它提供了 C++ 析構(gòu)函數(shù)的一些功能。但是,finalize() 并不完全與 C++ 的析構(gòu)函數(shù)一樣,并可以假設(shè)它會(huì)導(dǎo)致一系列的問題。finalize() 方法作用的一個(gè)關(guān)鍵元素是 Java 的垃圾回收器。

          垃圾回收器

          在 C/C++、Pascal和其他幾種多種用途的編程語言中,開發(fā)者有責(zé)任在內(nèi)存管理上發(fā)揮積極的作用。例如,如果你為一個(gè)對(duì)象或數(shù)據(jù)結(jié)構(gòu)分配了內(nèi)存,那么當(dāng)你不再使用它時(shí)必須釋放掉該內(nèi)存。

          在 Java 中,當(dāng)你創(chuàng)建一個(gè)對(duì)象時(shí),Java 虛擬機(jī)(JVM)為該對(duì)象分配內(nèi)存、調(diào)用構(gòu)造函數(shù)并開始跟蹤你使用的對(duì)象。當(dāng)你停止使用一個(gè)對(duì)象(就是說,當(dāng)沒有對(duì)該對(duì)象有效的引用時(shí)),JVM 通過垃圾回收器將該對(duì)象標(biāo)記為釋放狀態(tài)。

          當(dāng)垃圾回收器將要釋放一個(gè)對(duì)象的內(nèi)存時(shí),它調(diào)用該對(duì)象的finalize() 方法(如果該對(duì)象定義了此方法)。垃圾回收器以獨(dú)立的低優(yōu)先級(jí)的方式運(yùn)行,只有當(dāng)其他線程掛起等待該內(nèi)存釋放的情況出現(xiàn)時(shí),它才開始運(yùn)行釋放對(duì)象的內(nèi)存。(事實(shí)上,你可以調(diào)用System.gc() 方法強(qiáng)制垃圾回收器來釋放這些對(duì)象的內(nèi)存。)

          在以上的描述中,有一些重要的事情需要注意。首先,只有當(dāng)垃圾回收器釋放該對(duì)象的內(nèi)存時(shí),才會(huì)執(zhí)行finalize()。如果在 Applet 或應(yīng)用程序退出之前垃圾回收器沒有釋放內(nèi)存,垃圾回收器將不會(huì)調(diào)用finalize()。

          其次,除非垃圾回收器認(rèn)為你的 Applet 或應(yīng)用程序需要額外的內(nèi)存,否則它不會(huì)試圖釋放不再使用的對(duì)象的內(nèi)存。換句話說,這是完全可能的:一個(gè) Applet 給少量的對(duì)象分配內(nèi)存,沒有造成嚴(yán)重的內(nèi)存需求,于是垃圾回收器沒有釋放這些對(duì)象的內(nèi)存就退出了。

          顯然,如果你為某個(gè)對(duì)象定義了finalize() 方法,JVM 可能不會(huì)調(diào)用它,因?yàn)槔厥掌鞑辉尫胚^那些對(duì)象的內(nèi)存。調(diào)用System.gc() 也不會(huì)起作用,因?yàn)樗鼉H僅是給 JVM 一個(gè)建議而不是命令。

          finalize() 有什么優(yōu)點(diǎn)呢?

          如果finalize() 不是析構(gòu)函數(shù),JVM 不一定會(huì)調(diào)用它,你可能會(huì)疑惑它是否在任何情況下都有好處。事實(shí)上,在 Java 1.0 中它并沒有太多的優(yōu)點(diǎn)。

          根據(jù) Java 文檔,finalize() 是一個(gè)用于釋放非 Java 資源的方法。但是,JVM 有很大的可能不調(diào)用對(duì)象的finalize() 方法,因此很難證明使用該方法釋放資源是有效的。

          Java 1.1 通過提供一個(gè)System.runFinalizersOnExit() 方法部分地解決了這個(gè)問題。(不要將這個(gè)方法與 Java 1.0 中的System.runFinalizations() 方法相混淆。)不象System.gc() 方法那樣,System.runFinalizersOnExit() 方法并不立即試圖啟動(dòng)垃圾回收器。而是當(dāng)應(yīng)用程序或 Applet 退出時(shí),它調(diào)用每個(gè)對(duì)象的finalize() 方法。

          正如你可能猜測(cè)的那樣,通過調(diào)用System.runFinalizersOnExit() 方法強(qiáng)制垃圾回收器清除所有獨(dú)立對(duì)象的內(nèi)存,當(dāng)清除代碼執(zhí)行時(shí)可能會(huì)引起明顯的延遲?,F(xiàn)在建立一個(gè)示例 Applet 來演示 Java 垃圾回收器和finalize() 方法是如何相互作用的。

          回收垃圾

          通過使用Java Applet Wizard 創(chuàng)建一個(gè)新的 Applet 開始。當(dāng)提示這樣做時(shí),輸入 final_things 作為 Applet 名,并選擇不要生成源文件注釋。

          接下來,在Java Applet Wizard 進(jìn)行第三步,不要選擇多線程選項(xiàng)。在第五步之前,根據(jù)需要修改 Applet 的描述。

          當(dāng)你單擊Finish 后,Applet Wizard 將生成一個(gè)新的工作空間,并為該項(xiàng)目創(chuàng)建缺省的 Java 文件。從列表 A 中選擇適當(dāng)?shù)拇a輸入(我們已經(jīng)突出顯示了你需要輸入的代碼)。

          當(dāng)你完成代碼的輸入后,配置Internet 瀏覽器將System.out 的輸出信息寫到Javalog.txt 文件中。(在IE 選項(xiàng)對(duì)話框的高級(jí)頁面中選擇起用 Java Logging。)

          編譯并運(yùn)行該 Applet。然后,等待 Applet 運(yùn)行(你將在狀態(tài)欄中看到 Applet 已啟動(dòng)的信息),退出瀏覽器,并打開Javalog.txt 文件。你將會(huì)發(fā)現(xiàn)類似于下列行的信息:

                  1000 things constructed

                  0 things finalized

          正如你能夠看到的那樣,建立了1,000個(gè)對(duì)象仍然沒有迫使垃圾回收器開始回收空間,即使在 Applet 退出時(shí)也沒有對(duì)象被使用。

          現(xiàn)在,刪除在stop() 方法第一行中的注釋符以起用System.gc() 方法。再次編譯并運(yùn)行該 Applet ,等待 Applet 完成運(yùn)行,并退出瀏覽器。當(dāng)你再次打開Javalog.txt 文件,你將看到下列行:

                  1000 things constructed

                  963 things finalized

          這次,垃圾回收器認(rèn)為大多數(shù)對(duì)象未被使用,并將它們回收。按順序,當(dāng)垃圾回收器開始釋放這些對(duì)象的內(nèi)存時(shí),JVM 調(diào)用它們的finalize() 方法。

          繼承finalize()?

          順便,如果你在類中定義了finalize() ,它將不會(huì)自動(dòng)調(diào)用基類中的方法。在我們討論了finalize() 與 C++ 的析構(gòu)函數(shù)的不同點(diǎn)后,對(duì)這個(gè)結(jié)論不會(huì)驚訝,因?yàn)闉槟硞€(gè)類定制的清除代碼另一個(gè)類不一定會(huì)需要。

          如果你決定要通過派生一個(gè)類的finalize() 方法來調(diào)用基類中的finalize() 方法,你可以象其他繼承方法一樣處理。

                  protected void finalize()

                  {

                    super.finalize();

                    // other finalization code...

                  }

          除了允許你控制是否執(zhí)行清除操作外,這個(gè)技術(shù)還使你可以控制當(dāng)前類的finalize() 方法何時(shí)執(zhí)行。

          結(jié)論

          然而有益的是,Java 的自動(dòng)垃圾回收器不會(huì)失去平衡。作為便利的代價(jià),你不得不放棄對(duì)系統(tǒng)資源釋放的控制。不象 C++ 中的析構(gòu)函數(shù),Java Applet 不會(huì)自動(dòng)執(zhí)行你的類中的finalize() 方法。事實(shí)上,如果你正在使用 Java 1.0,即使你試圖強(qiáng)制它調(diào)用finalize() 方法,也不能確保將調(diào)用它。

          因此,你不應(yīng)當(dāng)依靠finalize() 來執(zhí)行你的 Applet 和應(yīng)用程序的資源清除工作。取而代之,你應(yīng)當(dāng)明確的清除那些資源或創(chuàng)建一個(gè)try...finally 塊(或類似的機(jī)制)來實(shí)現(xiàn)。

          列表 A: final_things.java

          import java.applet.*;

          import java.awt.*;

           

          class thing

          {

            public static int thingcount = 0;

            public static int thingfinal = 0;

           

            public thing()

            {

              ++thingcount;

            }

           

           

            protected void finalize()

            {

              ++thingfinal;

            }

          }

           

          public class final_things extends Applet

          {

           

            public final_things()

            {

            }

           

            public String getAppletInfo()

            {

              return "Name: final_thing\r\n" +

                     "Author: Tim Gooch\r\n" +

                     "Created with Microsoft " +

                     "Visual J++ Version 1.1";

            }

           

           

            public void init()

            {

                resize(320, 240);

           

            }

           

            public void destroy()

            {

            }

           

            public void paint(Graphics g)

            {

              g.drawString("Created with Microsoft" +

                "Visual J++ Version 1.1", 10, 20);

            }

           

            public void start()

            {

              while(thing.thingfinal < 1)

              {

                new thing();

              }

            }

           

            public void stop()

            {

              // System.gc();

              System.out.println(thing.thingcount +

                " things constructed");

              System.out.println(thing.thingfinal +

                " things finalized");

            }

           

          }


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           

          posts - 51, comments - 17, trackbacks - 0, articles - 0

          Copyright © 笨蛋啊帆

          主站蜘蛛池模板: 凤城市| 华安县| 肥东县| 会泽县| 慈溪市| 北碚区| 青阳县| 利川市| 张家界市| 海口市| 和政县| 大荔县| 五华县| 曲阳县| 社会| 衡阳县| 惠东县| 汪清县| 手游| 临湘市| 阿克苏市| 上思县| 黄浦区| 黔东| 洛宁县| 砚山县| 甘谷县| 平度市| 凤凰县| 定西市| 海淀区| 和林格尔县| 隆回县| 共和县| 苏州市| 车险| 元朗区| 秀山| 喀什市| 银川市| 博白县|