zoukankan      html  css  js  c++  java
  • JVM GC-----4、finalize()方法

     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中,对象的生命周期基本是这样的:
    1、类文件编程成功后,编译器会判断当前类是否重写了finalize()方法。如果已经重写了,那么会给当前类打上一个标识:has_finalizer
    2、对象在JVM创建后,会根据这个标识,同时再创建一个Finalizer对象。Finalizer对象会持有当前对象的引用。接着JVM会将Finalizer对象放置到一个容器(Finalizer.unfinalized)中。这个容器中的所有的Finalizer对象所持有的对象都没有被GC执行过重写的finalize()方法。
    3、在对象根据可达性分析判定需要被回收时,GC会从容器(Finalizer.unfinalized)中获得到回收对象所对应的Finalizer对象,并放置到另外一个对列中:F-QUEUE
    4、在GC机制中,有一个低优先级的守护线程:FinalizerThread。这个线程是专门用来执行finlize()方法的线程。它会从F-QUEUE中依次的获取Finalizer对象,然后执行Finalizer对象所对应的回收对象重写的finalize()方法。
    注意由于Finalizer对象是持有回收对象的引用的,因此在finalize()方法的执行过程中,是可以重新设置对象的引用到一个不会被回收的对象的属性上的,最终阻止当前对象被回收的。
    5、在finalize()方法被执行后,对应的Finalizer对象会被从最初的容器(Finalizer.unfinalized)移除掉。
    6、由于对象可能在第四步中重新被其他对象持有,因此需要重新确认一下这些对象。这时候GC会重新扫描一下F-QUEUE中所对应的对象。把仍然需要回收的对象回收掉。
    这里有一下情况需要注意:
    (1)由于容器(Finalizer.unfinalized)已经在第五步中移除掉了Finalizer对象,因此未来对象仍要被回收时,是不会再被调用到finalize()方法的。也就是说一个对象的finalize()方法只会被回收一次,无论这个对象是否是回收后又“重生”的。
    (2)finalize()方法是单线程串行回收的,所以如果finalize()方法耗时或者死循环什么的就会影响其它对象的finalize()方法执行(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ ),因此也可以看出来finalize()方法的执行非常不确定。
    (3)finalize()方法如果没有执行完,尽管当前对象已经不被GC-ROOT持有,但是仍然不会被回收掉。这就导致了内存的泄露,未来可能会出现内存溢出。
    这里可以用如下的方法测试:

     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对象的属性再次访问到

  • 相关阅读:
    任务墙(6月3日)
    燃尽图(6月3日)
    6.1-6.2小结
    5月28日任务进展
    个人感悟
    代码评审
    如何用ZBrush确定头部五官的位置
    ZBrush中的纹理-水手该怎样进行绘制
    怎样对ZBrush中的材料进行渲染和着色
    快速熟悉Zbrush中的四种裁切笔刷
  • 原文地址:https://www.cnblogs.com/jilodream/p/9236245.html
Copyright © 2011-2022 走看看