首頁  >  文章  >  Java  >  Java物件銷毀和finalize方法怎麼使用

Java物件銷毀和finalize方法怎麼使用

王林
王林轉載
2023-04-15 09:58:05738瀏覽

物件的銷毀

在C 中析構方法用於釋放資源並且銷毀物件本身。

在Java中,由於GC的存在,我們不需要手動回收內存,這大大減少了工作量,也提高了程式的安全性。但是Java也確實存在一個類似C 中析構的函數。

finalize方法

重載該方法,用於在類別被GC回收的時候執行一些操作。

下面是一個類別實作finalize的範例。

Aoo類別具有一個int 一個String屬性,重載了toString並且在建構其中列印這個物件及其建立時間,在finalize中列印這個物件及呼叫時間。

Aoo類別

public class Aoo {
       private int id;
       private String name;

       public Aoo(){
              this(0, null);
       }

       public Aoo(int id, String name){
              this.id = id;
              this.name = name;
              System.out.println(this.toString() + " now create:" + System.currentTimeMillis());
       }
        /*
        * 省略get/set/toString
        */
       protected void finalize() throws Throwable{
              super.finalize();
              System.out.println(this.toString() + "now finalize:" + System.currentTimeMillis());
       }
}

首先,一個簡單的測試

main方法

public class FinalizeTest {
       public static void main(String[] args) throws Exception {
              Aoo a = new Aoo(1, "a");
              a = null;
              System.gc()
              Thread.sleep(2000);
              System.exit(0);
       }
}

列印結果:

id:1 name:a now create:1497547723036
id:1 name:anow finalize:1497547724059 

GC對對象的回收

這裡手動呼叫了GC來清理內存,而如果將其註釋掉System.gc();,打印結果是這樣的:

id:1 name:a now create:1497547846923

也就是說,在沒有刻意調用GC的情況下,finalize方法根本沒有被調用,也就是說這個物件根本沒有被主動回收。

和想像中的不同,GC的運作方式是惰性的,也就是說,在記憶體沒有一處的情況下,GC不會去主動回收對象,為了驗證這個想法,我創建了一個線程,用於不斷的消耗內存,並且不主動調用GC。

ThreadA類別

public class ThreadA implements Runnable{
       public void run() {
              List<Integer> list = new ArrayList<Integer>();
              int i = 0;
              while(true){
                     list.add(i);
                     i++;
              }
       }
}

main方法

public class FinalizeTest {
       public static void main(String[] args) throws Exception {
              Aoo a = new Aoo(1, "a");
              a = null;
              ThreadA ta = new ThreadA();
              Thread t = new Thread(ta);
              t.start();
              Thread.sleep(2000);
              System.exit(0);
       }
}

列印結果:

id: 1 name:a now create:1497548135268
id:1 name:anow finalize:1497548135386

#這一次儘管沒有手動呼叫GC,但是finalize方法仍然運行了,也就是說,只有在

##這一次儘管沒有手動呼叫GC,但是finalize方法仍然運行了,也就是說,只有在

##這一次儘管沒有手動呼叫GC,但是finalize方法仍然運行了,也就是說,只有在

##這一次儘管沒有手動呼叫GC,但是finalize方法仍然運行了,也就是說,只有在記憶體被消耗、需要GC出面清理記憶體的時候,GC才會運作。

這樣的finalize方法確實不可靠,連能不能被呼叫都不一定,更不用說完成什麼特定的操作了,如果需要關流等回收資源,不如手動調用一個方法,或者在final區塊中統一釋放資源。

在finalize方法中,是否重新給自己一個引用來避免被GC回收?

嘗試在finalize方法中重新引用來讓GC無法回收

修改後的Aoo如下

public class Aoo {
       public static Aoo SAVE = null;
       private int id;
       private String name;

       public Aoo(){
              this(0, null);
       }

       public Aoo(int id, String name){
              this.id = id;
              this.name = name;
              System.out.println(this.toString() + " now create:" + System.currentTimeMillis());
       }  
       /*
        * 省略get/set/toString
        */
       protected void finalize() throws Throwable{
              super.finalize();
              System.out.println(this.toString() + "now finalize:" + System.currentTimeMillis());
              SAVE = this;
       }
}

main方法

public class FinalizeTest {
       public static void main(String[] args) throws Exception {
              Aoo.SAVE = new Aoo(1, "a");
              Aoo.SAVE = null;
              System.gc();
              Thread.sleep(500);
              System.out.println(Aoo.SAVE == null? "a is dead" : "a is alive" );              
              System.exit(0);             
       }
}

列印結果:

id:1 name:a now create:1497551409195
id:1 name:anow finalize:1497551409201

a is aliveanow finalize:1497551409201

a is aliveanow finalize:1497551409201

a is aliveanow finalize:1497551409201a is aliveanow finalize:1497551409201a is aliveanow 」>

這裡看出,Aoo.SAVE物件確實“復活了”,不過這樣的操作是有限制的,如果故技重施不會再一次“復活”該物件。

main方法

public class FinalizeTest {
       public static void main(String[] args) throws Exception {
              Aoo.SAVE = new Aoo(1, "a");
              Aoo.SAVE = null;
              System.gc();
              Thread.sleep(500);
              System.out.println(Aoo.SAVE == null? "a is dead" : "a is alive" );

              Aoo.SAVE = null;
              System.gc();
              Thread.sleep(500);
              System.out.println(Aoo.SAVE == null? "a is dead" : "a is alive" );

              System.exit(0);          
       }
}

列印結果:

id:1 name:a now create:1497551587715

id:1 name :anow finalize:1497551587721

a is alive

a is dead

這裡注意到,兩次的操作是相同的,而finalize方法只會被系統呼叫一次。

如果finalze方法中出現死迴圈會發生什麼事?

Aoo類別

public class Aoo {
       private int id;
       private String name;

       public Aoo(){
              this(0, null);
       }

       public Aoo(int id, String name){
              this.id = id;
              this.name = name;
              System.out.println(this.toString() + " now create:" + System.currentTimeMillis());
       }
       /*
        * 省略get/set/toString
        */
       protected void finalize() throws Throwable{
              super.finalize();
              while(true){
                     System.out.println(this.toString() + "now finalize:" + System.currentTimeMillis());
                     Thread.sleep(100);
              }
       }
}

main方法

public class FinalizeTest {
       public static void main(String[] args) throws Exception {
              Aoo a1 = new Aoo(1 , "a1");
              Aoo a2 = new Aoo(2 , "a2");
              a1 = null;
              a2 = null;
              ThreadA ta = new ThreadA();
              Thread t = new Thread(ta);
              t.start();
              Thread.sleep(5000);

              System.exit(0);
       }
}

列印結果:


id: 1 name:a1 now create:1497552024252

id:2 name:a2 now create:1497552024252
id:1 name:a1now finalize:1497552024373##id:a1now 1 name:a1now finalize:1497552026848

id:1 name:a1now finalize:1497552028960

id:1 name:a1now finalize:1497552032363#id:1 name:a1now finalize:1497552032363#id:1 name:a1now finalize:1497552032363#id:1

#p a1的finalize,有的時候執行的是a2的。

這個結果說明了兩點:

1.finalze方法在的執行緒優先權很低,時間間隔相當的不確定且明顯大於100毫秒。

2.這個死迴圈導致了別的物件的finalize方法無法執行。

如果物件的建立出現這種死循環,會不會導致物件無法銷毀進而導致記憶體溢出?

我們大量創建Aoo對象,並且等待GC自己回收記憶體。

為了直覺的觀看finalize方法的呼叫情況,刪除掉了Aoo物件初始化的時候的列印程式碼。

main方法

public class FinalizeTest {
       public static void main(String[] args) throws Exception {
              int i = 1;
              while(true){
                     Aoo a = new Aoo(i , "a" + i);
                     i++;
              }
       }
}
###讓程式執行了約兩分鐘,然後手動終止,查看輸出###

1497554225913
id:269614名稱:a269614現在完成:1497554226151
id:269614名稱:a269614現在完成:14975542#7351642222164622222212212222122212221222122222221222#72221222122212221222222212222#76222#7222222222122122#722212222221222#7222221222#7222221222#72222221222#722221222#72222212222#7222221222#722222122122#72:現在完成名稱。 73 5
id:269614 姓名:a269614現在最終確定:1497554227836
id:269614名稱:a269614現在最終確定:1497554229586
id:269614名稱:1497554229586
id:269614名稱:#26961464212614名稱:#21961462219614名稱:#21961462219614名稱。 269614現在最終確定:149 7554229951
id:269614 名稱:a269614now最終化:1497554230051
id:269614名稱:a269614現在最終化:1497554230156212562626219621962196219621962196219621962196221962196219621962196219621962219622196221962219696221967622196962221962: 33699
id: 269614名稱:a269614現在最終化:1497554233800
id:269614 name:a269614現在最終確定: 1497554233900
id:269614名稱:a269614233900
#id:269614名稱:a2696142642614名稱:a269615462222614名稱:a269615462222614名稱:a21961546222222614名稱: 9614現在完成:1497554234408
id:269614名稱:a269614現在完成:1497554234508
id:269614 名稱:a269614現在完成:1497554235053
id:269614名稱:a269614現在最終確定:1497554235153
id:269614名稱:a269614現在最終確定:1497554235253
id:269614名稱:a269614現在最終確定:1497554235823
id:269614名稱:a269 614現在已確定:149755423592362126146752219642219622223222223222222222223:#1969696222237622222222223:#196962222372222237222223:#19696962: 36023
id :269614名稱:a269614現在最終確定:1497554240324
id:269614名稱:a269614現在最終確定:1497554240424
id:269614名稱:a2696 142642195756257575:#961457522525757575:269575722212575:26257575:#9695 269614現在完成:1497554241146
id:269614名稱:a269614現在完成:1497554241247
id:269614名稱:a269614現在完成:1497554241347#145726245724136214241347#175
id:269614 名稱: a269614現在最終確定:1497554242020
id:269614名稱:a269614現在最終確定:1497554242120
id:269614名稱:a269614現在最終確定名稱:149755426421546222146422122212642212222122222122222122222222222#:#264646222126 9 7554242321
id:269614 名稱:a269614now最終化:1497554242421
id:269614名稱:a269614現在最終化:1497554242521
id:269614名稱:a26961426961464269614名稱: a269614現在最終化:1497554248467
id:269614 name:a269614現在最終確定: 1497554248567
id:269614名稱:a269614現在完成:14975542486614名稱:a269614現在完成:1497554248667642216764221967642167676221967676767622217676722217676722217676222:現在完成名稱:現在完成名稱。 9534
id:269614名稱:a269614現在完成:1497554249634
id:269614 名稱:a269614現在完成:1497554249734
id:269614名稱:a269614現在最終確定:1496756496175649614現在最終。 :1497554255954
id:269614名稱:a269614現在最終確定:1497554256055
id:269614名稱:a269 614現已確定:1497554256155
id:269614名稱:a269614現在最終確定:1497554256255
id:269614名稱:a269614現在最終確定:1497554256356
id:269614名稱:a269614現在最終確定:1497554257285
id:269614名稱:a2696 14現在最終確定:149755425738664#a38664#a386964257219625219695219692196921969219697625196976:現在完成名稱。 57486
id:269614名稱:a269614現在完成:1497554257586
id:269614名稱:a269614現在完成:1497554257686
id:269614名稱:a269614257686
id:269614542696142576862215462226226222226222212622222622:262622222222:26462222623:2682222:64623:26262262:現在確定名稱: 9614現在最終確定: 1497554268753
id:269614名稱:a269614現在最終確定:1497554268853
id:269614名稱:a269614現在最終確定:1497554268953142689531422622196422686921426864222:現在54
id:269614名稱:a269614now最終化:1497554269154
id:269614名稱:a269614現在最終化:1497554277474
id:269614名稱:a269614現在最終化:14967654215614現在最終化名稱: :1497554301062



可以發現兩種情況:

##1.只有一個物件的finalize方法被執行了,而這個死迴圈的finalize方法阻止了其他物件執行finalize方法

2.程式執行很快但是時間長了之後,finalize方法就開始執行,隨著記憶體消耗的不斷增加,finalize方法被執行的次數也越來越少。

以上是Java物件銷毀和finalize方法怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除