zoukankan      html  css  js  c++  java
  • final、finally和finalize(三)

    finalize 是Java中由 JVM 在垃圾回收的时候执行的一个方法。周志明老师在《深入理解Java虚拟机》中进行了细致的讲解,我在这里引用一下周老师书中的内容,同时感谢前辈分享您的知识,让晚辈受益匪浅。

    finalize 这个方法可以说是对象逃避被 JVM 回收的最后一搏。

    在正式说 finalize 之前,先说一下Java 用来判断对象是否存活的方法------根搜索算法(GC Roots Tracing)

           这个算法的基本思想是:通过一系列的名为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots 没有任何引用链的时候,则证明此对象是不可用的。这里有点像网页爬虫根据一个根网页顺着链接爬取其他网页,只要是在互联网上的页面总会和其他的页面有链接,否则就不用放到网上了。

    知道了JVM 的回收策略,我们再来说一下 finalize 这个方法是在什么时候用的。


      

    在根搜索算法中引用不到的对象其实不是马上就被回收的,就和犯了罪不能马上拉出去枪毙一样,它还有一次改过自新的机会,这个机会就是 finalize 这个方法。要真正回收一个对象首先前提就是它没有与GC Roots相链接的任何引用链了,这个时候 JVM 会对其进行第一次标记并且进行一次筛选,筛选的条件就是看是否还需要给这个对象一次死里逃生的机会。当对象没有覆盖 finalize 方法或者是finalize方法已经被执行过一次的时候(finalize方法只能被执行一次),JVM 会判定此对象没了引用链也没了最后一块免死金牌(finalize方法),那是必死无疑了。

    如果说这个对象覆盖了 finalize 方法,并且这个方法还没有被执行过,这时候,这个对象会被放置在一个名为 F-Queue 的队列之中,并在稍后一条由 JVM 自动建立的、低优先级的Finalizer线程去执行。如果在执行 finalize 方法的时候对象想办法重新获得了与GC Roots的引用链,那么它就可以继续存活了。如果这次机会还是没把握住,那就真的没有机会了。毕竟 JVM 的内存有限,没有用的对象要尽快清理掉。


      

    下面我们来看一段对象用 finalize 方法自救的一个例子(借用周老师的例子):

    public class FinalizeEscapeGC {
            public static FinalizeEscapeGC SAVE_HOOK = null;
    
            public void isAlive() {
                System.out.println("yes, I'm still alize :)");
            }
    
            @Override
            protected void finalize() throws Throwable {
                // TODO Auto-generated method stub
                super.finalize();
                System.out.println("finalize method executed!");
                FinalizeEscapeGC.SAVE_HOOK = this; // 在执行finalize方法的时候重新获得与GC
                                                    // Roots的引用链
            }
    
            public static void main(String[] args) throws Throwable {
                SAVE_HOOK = new FinalizeEscapeGC();
    
                // 对象第一次成功拯救自己
                SAVE_HOOK = null;
                System.gc(); // 建议 JVM 回收垃圾对象
    
                Thread.sleep(500); // 因为Finalizer的优先级低,暂停0.5秒以等待它执行
                if (SAVE_HOOK != null) {
                    SAVE_HOOK.isAlive();
                } else {
                    System.out.println("no, I'm dead :(");
                }
    
                // 下面代码和上面完全相同,但这次自救却失败了
                SAVE_HOOK = null;
                System.gc(); // 建议 JVM 回收垃圾对象
    
                Thread.sleep(500); // 因为Finalizer的优先级低,暂停0.5秒以等待它执行
                if (SAVE_HOOK != null) {
                    SAVE_HOOK.isAlive();
                } else {
                    System.out.println("no, I'm dead :(");
                }
            }
    
        }

    运行结果:

    finalize method executed!
    yes, I'm still alize :)
    no, I'm dead :(


      

    从结果中我们可见,SAVE_HOOK对象的finalize 方法确实被执行,并且在执行的过程中,对象通过重新过得引用链得到了自救。但是在两次完全一样的代码(从第38行开始)第二次执行时,finalize 方法没有被第二次执行,对象不再能够自救。就是说finalize方法只能被执行一次,这也算是 JVM 在节省内存和减少对象频繁调入调出内存之间做的一个均衡吧。


      

    另外在最后说一下周老师对于 finalize 方法给大家的意见:……finalize()方法不是C/C++ 中的析构函数,而是Java刚诞生时为了使C/C++程序员更容易接受它所做出的一个妥协。它的运行代价高昂,不确定性大,无法保证各个对象的调用顺序。…… finalize()能做的所有的工作,使用 try-finally 或其他的方式都可以做得更好,更及时,大家完全可以忘掉Java语言中还有这个方法的存在。

  • 相关阅读:
    Go视频教程
    Mysql常用
    同步Redux
    React跨组件通讯
    React组件通讯
    React
    git生成公钥和私钥
    常用经典算法---希尔排序
    string和c_str()使用时的坑
    腾讯云python网站开发环境搭建
  • 原文地址:https://www.cnblogs.com/yuxiaoqi/p/2713439.html
Copyright © 2011-2022 走看看