java_finalize

        在java虚拟机运行时,一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法(如果定义了),并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以我们可以利用finalize(),在垃圾回收的时刻做一些清理工作。

        而finalize()并不像C++中的析构函数一样,对象一定会被销毁。对于没有用的对象何时被销毁,是由虚拟机中的GC机制决定的。

        此外,java虚拟机只知道释放那些由new分配的内存,所以finalize()还可以来清楚那些特殊的被分配的内存。这些不是由new分配的内存是如何产生的呢?这种情况主要发生在使用“本地方法”(native method)的时候。本地方法其实并不是由java编写的代码,是由C和C++编写的,而C和C++还能调用其他的语言,所以实际上可以调用任何代码。这样可以使java通过native方法实现更加底层的操作。C会调用malloc()之类的函数去分配内存,所以在finalize()中,应该调用free()去释放这些内存,而不要去指望GC了。

        最后解释一下第一段中描述的,为什么要等到下次垃圾回收时才能真正回收内存

首先,要搞清楚对象什么时候会被定义成需要清除

  1.引用计数法

       每个对象都含有一个引用计数器,当有引用连接至对象的时候,引用计数加1;当引用离开作用域或被置为了null时,引用计数减1,当某个对象的引用计数为0时,就释放其占用的空间。这种方式,简单快速,但存在着一个不好解决的问题,即几个都应该被回收的对象,相互循环引用,则引用计数永远都不会变为0。

 2.根搜索算法

        java虚拟机采用的是此算法。此算法的基本思路就是通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可以达)时,则证明此对象是不可用的。

  图中object5,object6,object7他们虽然相互关联,但连接不到GC Roots,所以他们需要被回收。

    在java中,有4种可以作为GC Roots 的对象:

1.虚拟机栈(栈帧中的本地变量表)中的引用的对象

2.方法区中的类静态属性引用的对象

3.方法区中的常量引用的对象

4.本地方法栈中JNI(即一般说的Native方法)

搞清楚了什么样的对象需要回收,那来看看GC后,这些对象是不是马上被回收

    其实并不是,如果对象覆盖了finalize(),被回收至少要经历两个标记过程,中途可以得到“拯救”。

    当第一次发生GC后,会先调用finalize()(前提覆盖了此方法),将此对象标记成需要回收,被放到了一个名叫F—Queue的队列中。之后如果此对象,重新能与引用链上的任何一个对象建立关联,则在第二次标记时,它将被移出“即将回收”的集合。如果没有“逃脱”,那么就离死不远啦。另外,finalize()只能被执行一次

    

class FinalizeEscapeGC{

    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive(){
        System.out.println("yes,i am still alive");
    }

    protected void finalize() throws Throwable{
        super.finalize();
        System.out.println("finalize method executed");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }
}

public class Main {

    public static void main(String[] args) throws InterruptedException {
        FinalizeEscapeGC.SAVE_HOOK = new FinalizeEscapeGC();

        FinalizeEscapeGC.SAVE_HOOK = null;
        System.gc();

        Thread.sleep(50);
        if(FinalizeEscapeGC.SAVE_HOOK != null){
            FinalizeEscapeGC.SAVE_HOOK.isAlive();
        }else {
            System.out.println("no,i am dead");
        }
        
        //下面方法完全一样,但自救失败,因为finalize方法只能执行一次
        
        FinalizeEscapeGC.SAVE_HOOK = null;
        System.gc();

        Thread.sleep(50);
        if(FinalizeEscapeGC.SAVE_HOOK != null){
            FinalizeEscapeGC.SAVE_HOOK.isAlive();
        }else {
            System.out.println("no,i am dead");
        }
    }
}

 运行结果:


    目前finalize()能做的所有工作,使用try-finally能做得更好、更及时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值