finalize()方法是Object類中定義的protect方法。每個類均可以重寫該方法,給出本身的實現。當類在被回收期間,這個方法就可能會被調用到。編程
爲何說可能?
這是因爲finalize()的調用時機甚至是否會被調用到都存在着太多的不肯定性。基於這個緣由,幾乎全部的技術書籍及文章都不推薦開發人員依賴重寫finalize()方法來作什麼事情。反而是建議開發人員寫一個相似析構函數的方法,在對象調用完畢後,手動的執行本身添加的這個方法。對於軟件開發這種有時須要很是精確掌控進度的工做,單純的依賴GC和finalize()方法來控制,很是的困難。
finalize()方法是Java誕生初期,爲了推廣Java語言,兼容C++使用語法,而作出的讓步。對編程稍有了解的人都應該知道C/C++語法都是在結束對象生命週期時,手動的調用析構函數。然而Java等擁有GC機制的語言並不會實時的清理內存,而是在內存分配出現緊張的(防盜鏈接:本文首發自http://www.cnblogs.com/jilodream/ )狀況下才會回收。這就致使了回收時機的不肯定性,也是Java的finalize()方法和C/C++的析構函數有本質區別的地方。
在Java中,對象的生命週期基本是這樣的:
一、類文件編程成功後,編譯器會判斷當前類是否重寫了finalize()方法。若是已經重寫了,那麼會給當前類打上一個標識:has_finalizer
二、對象在JVM建立後,會根據這個標識,同時再建立一個Finalizer對象。Finalizer對象會持有當前對象的引用。接着JVM會將Finalizer對象放置到一個容器(Finalizer.unfinalized)中。這個容器中的全部的Finalizer對象所持有的對象都沒有被GC執行太重寫的finalize()方法。
三、在對象根據可達性分析斷定須要被回收時,GC會從容器(Finalizer.unfinalized)中得到到回收對象所對應的Finalizer對象,並放置到另一個對列中:F-QUEUE。
四、在GC機制中,有一個低優先級的守護線程:FinalizerThread。這個線程是專門用來執行finlize()方法的線程。它會從F-QUEUE中依次的獲取Finalizer對象,而後執行Finalizer對象所對應的回收對象重寫的finalize()方法。
注意因爲Finalizer對象是持有回收對象的引用的,所以在finalize()方法的執行過程當中,是能夠從新設置對象的引用到一個不會被回收的對象的屬性上的,最終阻止當前對象被回收的。
五、在finalize()方法被執行後,對應的Finalizer對象會被從最初的容器(Finalizer.unfinalized)移除掉。
六、因爲對象可能在第四步中從新被其餘對象持有,所以須要從新確認一下這些對象。這時候GC會從新掃描一下F-QUEUE中所對應的對象。把仍然須要回收的對象回收掉。
這裏有一下狀況須要注意:
(1)因爲容器(Finalizer.unfinalized)已經在第五步中移除掉了Finalizer對象,所以將來對象仍要被回收時,是不會再被調用到finalize()方法的。也就是說一個對象的finalize()方法只會被回收一次,不管這個對象是不是回收後又「重生」的。
(2)finalize()方法是單線程串行回收的,因此若是finalize()方法耗時或者死循環什麼的就會影響其它對象的finalize()方法執行(防盜鏈接:本文首發自http://www.cnblogs.com/jilodream/ ),所以也能夠看出來finalize()方法的執行很是不肯定。
(3)finalize()方法若是沒有執行完,儘管當前對象已經不被GC-ROOT持有,可是仍然不會被回收掉。這就致使了內存的泄露,將來可能會出現內存溢出。
這裏能夠用以下的方法測試:ide
1 public class GCFinalizer 2 { 3 String name; 4 String[] kStrings = new String[1024*64]; 5 6 public static void main(String[] args) throws InterruptedException 7 { 8 boolean isClear = true; 9 for (int i = 0; i < 10000; i++) 10 { 11 System.out.println(isClear + "Name" + (i - 1)); 12 GCFinalizer gcf = new GCFinalizer(); 13 gcf.name = "Name" + i; 14 gcf = null; 15 System.gc(); 16 Thread.sleep(500); 17 } 18 } 19 20 protected void finalize() throws InterruptedException 21 { 22 while (true) 23 { 24 System.out.println(name); 25 Thread.sleep(1000); 26 } 27 } 28 }
結果以下圖函數
使用內存監視工具查看到的內存曲線:工具
(4)若是A對象持有B對象,A引用被外界斷開。可是A複寫了finalize()方法,方法中A對象被GC—ROOT對象再次持有,那麼這時候B是否已經被回收掉了呢?
這裏能夠用以下的方法測試:測試
1 public class GCF 2 { 3 public String name; 4 5 public GCF(String name) 6 { 7 this.name = name; 8 } 9 10 public GCF subGCF; 11 12 static GCF ref; 13 14 public static void main(String[] args) throws InterruptedException 15 { 16 GCF gcf = new GCF("masterName"); 17 gcf.subGCF = new GCF("subName"); 18 gcf = null; 19 System.gc(); 20 Thread.sleep(3000); 21 gcf = ref; 22 System.out.println("step1"); 23 System.out.println("is Sub GCF Obj exist:" + (gcf.subGCF != null)); 24 gcf = null; 25 ref = null; 26 System.gc(); 27 Thread.sleep(1000); 28 System.out.println("step2"); 29 gcf = ref; 30 System.out.println("is reborn:" + (gcf != null)); 31 while (true) 32 { 33 Thread.sleep(1000); 34 } 35 } 36 37 @Override 38 protected void finalize() throws InterruptedException 39 { 40 System.out.println(name); 41 if (name.equals("masterName")) 42 { 43 ref = this; 44 } 45 } 46 }
經過返回結果咱們能夠知道,在A對象被從新持有以(防盜鏈接:本文首發自http://www.cnblogs.com/jilodream/ )後,B對象沒有被回收掉:
更準確的說是:B對象發生了回收,可是仍然能夠經過A對象的屬性再次訪問到this