這篇文章為大家帶來了關於java的相關知識,主要介紹了Java之對象銷毀和finalize方法的使用,具有很好的參考價值,下面一起來看一下,希望對大家有幫助。
推薦學習:《java影片教學》
在C 中析構方法用於釋放資源並且銷毀物件本身。
在Java中,由於GC的存在,我們不需要手動回收內存,這大大減少了工作量,也提高了程式的安全性。但是Java也確實存在一個類似C 中析構的函數。
重載該方法,用於在類別被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來清理內存,而如果將其註釋掉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方法
id:1 name:anow finalize:1497551409201public 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
a is aliveanow finalize:1497551409201
a is aliveanow finalize:1497551409201a 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這裡注意到,兩次的操作是相同的,而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); } } }
id:1 name:a1now finalize:1497552024373##id:a1now 1 name:a1now finalize:1497552026848main方法
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:1497552024252id:2 name:a2 now create:1497552024252
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毫秒。
如果物件的建立出現這種死循環,會不會導致物件無法銷毀進而導致記憶體溢出?
我們大量創建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方法被執行的次數也越來越少。至於為什麼會這樣,我不知道= =
#總結
至此,我嘗試了finalize方法的一些解決和特殊情況。可以看出,GC調用finalize方法存在巨大的不確定性,確實很不可靠,不過透過這個方法,了解了一些關於GC的知識,也讓我明白,雖然Java語言雖然具有高度了解的一致性等特點使得很容易上手,但是要做到對Java的精通,路還很遠呢~~
java影片教學》
以上是Java之物件銷毀和finalize方法的使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!