[Java]finalized方法

      今天底层报了一个null up call问题,查来查去发现是JNI没有保持对象的引用,然后被JVM回收了,导致出现了空指针。在查问题的过程中提交了C++的析构函数,然后映射到了finalized(),所以顺便研究了了一下,为了以后可以看到,写下来吧。

      写在开始:这个方法官方都不推荐了,所以知道原理就可以了,没必要太深入了解。面试的时候finalized()经常跟final和finally放一块考察,仅仅是因为名字像,而不是功能相近(之前有一次面试确实问到了,不过只有一次)。事实上finalized()在开发中很少用到,而且官方也不推荐使用(好像是在1.9版本里就不允许使用了),据说这个方法设计的目的就是向C++妥协才有的这个方法。下面开始正题:

      C++中的析构函数可以直接释放内存,那么是不是finalized()也可以呢?不是的,Java的内存回收机制只有GC,所以这里不要搞错了,并不是显式的调用了这个方法就可以释放内存。那么下一个问题就来了,什么时候可以释放内存呢?Java的回收机制里判定,如果从根root无法连接到这个对象,那么这个对象的内存空间就会被回收,这里有一个概念就是“从根root可以连接到这个对象”(JVM GC的回收机制里的概念)。我们把可以连接到这个对象的状态称为reachable,连接不到的状态称为unreachable,这里已经有两个状态了,再加上一个状态:f-reachable,也就是从root无法直接连接而通过finalizable连接到的状态。f-reachale这个状态很有意思,它就是所谓的“复活”的概念,网上例子很多,这里就不再赘述了。这里出现了三种状态:reachable、f-reachable和unreachable,然后涉及finalize也存在三种状态:unfinalized、finalizable、finalized,从名字上就可以判断了。然后进行排列组合,reachable对应三种状态,f-reachable对应三种状态,unreachable对应两种状态,这里是不是有点问题,为什么unreachable对应的是两种状态?因为java的机制里unreachable + finalizable的是不会存在的,下面上图例:

     

变迁说明:

  1. 新建对象首先处于[reachable, unfinalized]状态
  2. 随着程序的运行,一些引用关系会消失,导致状态变迁,从reachable状态变迁到f-reachable或unreachable状态
  3. 若JVM检测到处于unfinalized状态的对象变成f-reachable或unreachable,JVM会将其标记为finalizable状态。若对象原处于[unreachable, unfinalized]状态,则同时将其标记为f-reachable。
  4. 在某个时刻,JVM取出某个finalizable对象,将其标记为finalized并在某个线程中执行其finalize方法。由于是在活动线程中引用了该对象,该对象将变迁到(reachable, finalized)状态。该动作将影响某些其他对象从f-reachable状态重新回到reachable状态
  5. 处于finalizable状态的对象不能同时是unreahable的,由第4点可知,将对象finalizable对象标记为finalized时会由某个线程执行该对象的finalize方法,致使其变成reachable。这也是图中只有八个状态点的原因
  6. 程序员手动调用finalize方法并不会影响到上述内部标记的变化,因此JVM只会至多调用finalize一次,即使该对象“复活”也是如此。程序员手动调用多少次不影响JVM的行为
  7. 若JVM检测到finalized状态的对象变成unreachable,回收其内存
  8. 若对象并未覆盖finalize方法,JVM会进行优化,直接回收对象
  9. 注:System.runFinalizersOnExit()等方法可以使对象即使处于reachable状态,JVM仍对其执行finalize方法

    这里就解释完了,最后附上一个讲解复活方法讲解的比较详细的代码:
    public class GC { 
       
        public static GC SAVE_HOOK = null; 
       
        public static void main(String[] args) throws InterruptedException {
            // 新建对象,因为SAVE_HOOK指向这个对象,对象此时的状态是(reachable,unfinalized)
            SAVE_HOOK = new GC(); 
            //将SAVE_HOOK设置成null,此时刚才创建的对象就不可达了,因为没有句柄再指向它了,对象此时状态是(unreachable,unfinalized)
            SAVE_HOOK = null; 
            //强制系统执行垃圾回收,系统发现刚才创建的对象处于unreachable状态,并检测到这个对象的类覆盖了finalize方法,因此把这个对象放入F-Queue队列,由低优先级线程执行它的finalize方法,此时对象的状态变成(unreachable, finalizable)或者是(finalizer-reachable,finalizable)
            System.gc(); 
            // sleep,目的是给低优先级线程从F-Queue队列取出对象并执行其finalize方法提供机会。在执行完对象的finalize方法中的super.finalize()时,对象的状态变成(unreachable,finalized)状态,但接下来在finalize方法中又执行了SAVE_HOOK = this;这句话,又有句柄指向这个对象了,对象又可达了。因此对象的状态又变成了(reachable, finalized)状态。
            Thread.sleep(500);
            // 这里楼主说对象处于(reachable,finalized)状态应该是合理的。对象的finalized方法被执行了,因此是finalized状态。又因为在finalize方法是执行了SAVE_HOOK=this这句话,本来是unreachable的对象,又变成reachable了。
            if (null != SAVE_HOOK) { //此时对象应该处于(reachable, finalized)状态 
                // 这句话会输出,注意对象由unreachable,经过finalize复活了。
                System.out.println("Yes , I am still alive"); 
            } else { 
                System.out.println("No , I am dead"); 
            } 
            // 再一次将SAVE_HOOK放空,此时刚才复活的对象,状态变成(unreachable,finalized)
            SAVE_HOOK = null; 
            // 再一次强制系统回收垃圾,此时系统发现对象不可达,虽然覆盖了finalize方法,但已经执行过了,因此直接回收。
            System.gc(); 
            // 为系统回收垃圾提供机会
            Thread.sleep(500); 
            if (null != SAVE_HOOK) { 
                // 这句话不会输出,因为对象已经彻底消失了。
                System.out.println("Yes , I am still alive"); 
            } else { 
                System.out.println("No , I am dead"); 
            } 
        } 
       
        @Override 
        protected void finalize() throws Throwable { 
            super.finalize(); 
            System.out.println("execute method finalize()"); 
           // 这句话让对象的状态由unreachable变成reachable,就是对象复活
            SAVE_HOOK = this; 
        } 
    }  

     

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值