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