理解finalize()-解構函式替代者

來源:互聯網
上載者:User
函數
理解finalize()-解構函式替代者



 



在許多方面,Java 類似於 C++。Java 的文法非常類似於 C++,Java 有類、方法和資料成員;Java 的類有建構函式; Java 有異常處理。



 



    但是,如果你使用過 C++ 會發現 Java 也丟掉一些可能是你熟悉的特性。這些特性之一就是解構函式。取代使用解構函式,Java 支援finalize() 方法。



 



    在本文中,我們將描述 finalize() 與 C++ 解構函式的區別。另外,我們將建立一個簡單的 Applet 來示範 finalize() 是如何工作的。



 



最終的界限



 



    與 Java 不同,C++ 支援局部對象(基於棧)和全域對象(基於堆)。因為這一雙重支援,C++ 也提供了自動構造和析構,這導致了對建構函式和解構函式的調用,(對於堆對象)就是記憶體的分配和釋放。



 



    在 Java 中,所有對象都駐留在堆記憶體,因此局部對象就不存在。結果,Java 的設計者覺得不需要解構函式(象 C++ 中所實現的)。



 



    取而代之,Java 定義了一個特殊的方法叫做finalize() ,它提供了 C++ 解構函式的一些功能。但是,finalize() 並不完全與 C++ 的解構函式一樣,並可以假設它會導致一系列的問題。finalize() 方法作用的一個關鍵元素是 Java 的記憶體回收行程。



 



記憶體回收行程



 



    在 C/C++、Pascal和其他幾種多種用途的程式設計語言中,開發人員有責任在記憶體管理上發揮積極的作用。例如,如果你為一個對象或資料結構分配了記憶體,那麼當你不再使用它時必須釋放掉該記憶體。



 



    在 Java 中,當你建立一個對象時,JAVA 虛擬機器(JVM)為該對象分配記憶體、調用建構函式並開始跟蹤你使用的對象。當你停止使用一個對象(就是說,當沒有對該對象有效引用時),JVM 通過記憶體回收行程將該對象標記為釋放狀態。



 



    當記憶體回收行程將要釋放一個對象的記憶體時,它調用該對象的finalize() 方法(如果該對象定義了此方法)。記憶體回收行程以獨立的低優先順序的方式運行,只有當其他線程掛起等待該記憶體釋放的情況出現時,它才開始運行釋放對象的記憶體。(事實上,你可以調用System.gc() 方法強制記憶體回收行程來釋放這些對象的記憶體。)



 



    在以上的描述中,有一些重要的事情需要注意。首先,只有當記憶體回收行程釋放該對象的記憶體時,才會執行finalize()。如果在 Applet 或應用程式退出之前記憶體回收行程沒有釋放記憶體,記憶體回收行程將不會調用finalize()。



 



    其次,除非記憶體回收行程認為你的 Applet 或應用程式需要額外的記憶體,否則它不會試圖釋放不再使用的對象的記憶體。換句話說,這是完全可能的:一個 Applet 給少量的對象分配記憶體,沒有造成嚴重的記憶體需求,於是記憶體回收行程沒有釋放這些對象的記憶體就退出了。



 



    顯然,如果你為某個對象定義了finalize() 方法,JVM 可能不會調用它,因為記憶體回收行程不曾釋放過那些對象的記憶體。調用System.gc() 也不會起作用,因為它僅僅是給 JVM 一個建議而不是命令。



 



finalize() 有什麼優點呢?



 



    如果finalize() 不是解構函式,JVM 不一定會調用它,你可能會疑惑它是否在任何情況下都有好處。事實上,在 Java 1.0 中它並沒有太多的優點。



 



    根據 Java 文檔,finalize() 是一個用於釋放非 Java 資源的方法。但是,JVM 有很大的可能不調用對象的finalize() 方法,因此很難證明使用該方法釋放資源是有效。



 



      Java 1.1 通過提供一個System.runFinalizersOnExit() 方法部分地解決了這個問題。(不要將這個方法與 Java 1.0 中的System.runFinalizations() 方法相混淆。)不象System.gc() 方法那樣,System.runFinalizersOnExit() 方法並不立即試圖啟動記憶體回收行程。而是當應用程式或 Applet 退出時,它調用每個對象的finalize() 方法。



 



    正如你可能猜測的那樣,通過調用System.runFinalizersOnExit() 方法強制記憶體回收行程清除所有獨立對象的記憶體,當清除代碼執行時可能會引起明顯的延遲。現在建立一個樣本 Applet 來示範 Java 記憶體回收行程和finalize() 方法是如何相互作用的。



 



回收垃圾



 



    通過使用Java Applet Wizard 建立一個新的 Applet 開始。當提示這樣做時,輸入 final_things 作為 Applet 名,並選擇不要產生源檔案注釋。



 



    接下來,在Java Applet Wizard 進行第三步,不要選擇多線程選項。在第五步之前,根據需要修改 Applet 的描述。



 



    當你單擊Finish 後,Applet Wizard 將產生一個新的工作空間,並為該項目建立預設的 Java 檔案。從列表 A 中選擇適當的代碼輸入(我們已經反白了你需要輸入的代碼)。



 



    當你完成代碼的輸入後,配置Internet 瀏覽器將System.out 的輸出資訊寫到Javalog.txt 檔案中。(在IE 選項對話方塊的進階頁面中選擇起用 Java Logging。)



編譯並運行該 Applet。然後,等待 Applet 運行(你將在狀態列中看到 Applet 已啟動的資訊),退出瀏覽器,並開啟Javalog.txt 檔案。你將會發現類似於下列行的資訊:



 



        1000 things constructed



        0 things finalized



 



    正如你能夠看到的那樣,建立了1,000個對象仍然沒有迫使記憶體回收行程開始回收空間,即使在 Applet 退出時也沒有對象被使用。



 



    現在,刪除在stop() 方法第一行中的注釋符以起用System.gc() 方法。再次編譯並運行該 Applet ,等待 Applet 完成運行,並退出瀏覽器。當你再次開啟Javalog.txt 檔案,你將看到下列行:



 



        1000 things constructed



        963 things finalized



 



    這次,記憶體回收行程認為大多數對象未被使用,並將它們回收。按順序,當記憶體回收行程開始釋放這些對象的記憶體時,JVM 調用它們的finalize() 方法。



 



繼承finalize()?



 



    順便,如果你在類中定義了finalize() ,它將不會自動調用基類中的方法。在我們討論了finalize() 與 C++ 的解構函式的不同點後,對這個結論不會驚訝,因為為某個類定製的清除代碼另一個類不一定會需要。



 



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



 



        protected void finalize()



        {



          super.finalize();



          // other finalization code...



        }



 



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



 



結論



 



    然而有益的是,Java 的自動記憶體回收行程不會失去平衡。作為便利的代價,你不得不放棄對系統資源釋放的控制。不象 C++ 中的解構函式,Java Applet 不會自動執行你的類中的finalize() 方法。事實上,如果你正在使用 Java 1.0,即使你試圖強制它調用finalize() 方法,也不能確保將調用它。



 



    因此,你不應當依靠finalize() 來執行你的 Applet 和應用程式的資源清除工作。取而代之,你應當明確的清除那些資源或建立一個try...finally 塊(或類似的機制)來實現。



 



列表 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");



  }



 



}



 




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.