美文网首页
如何判断对象已死?

如何判断对象已死?

作者: 做梦枯岛醒 | 来源:发表于2019-04-03 12:59 被阅读0次

    在GC机制中,垃圾回收器在对对象进行回收之前,首先要确定哪些对象是存活的,哪些对象又是死去的。

    首先给出的方案是引用计数器方法,在这种方法中,当一个对象没有任何引用去引用它的话,就可以被判断不再被使用。
    具体的思路如下:给对象添加一个引用计数器,每当有一个地方引用它时,计数器数值就加1,当引用失效的时候,计数器数值减1,当计数器变为0的时候,就可以判断对象已经不再被使用。

    但有没有考虑到下面这样的例子?

    objA.instance = objB;
    objB.instance = objA;
    

    这样的互相引用,导致使用引用计数器的GC不能回收他们,但实际上如果Main方法中,没有用到他们呢?类似于一种死锁(类比可能不准确),谁也不放过谁,但是外界又没有引用,就导致资源的浪费。

    那么现在的Java用的是什么方案进行判断的呢?
    答案就是可达性分析.

    可达性分析中,有一个GC Roots,从GC Roots向下搜索,可以到达的对象,我们判断为不可回收,不可能到达的对象就判断为可回收。期间走过的路径,称为引用链。


    如上图,5,6,7三个对象就不可能通过GC Roots来访问到,所以就被判定为可回收。

    那么哪些对象可以作为可达性分析的GC Roots?

    • 方法区中的静态属性引用的对象
    • 方法区中的常量引用的对象
    • 虚拟机栈中引用的对象
    • 本地方法栈引用的对象

    要说这上面这个问题,也经常在面试中遇到过,那么到底为什么他们能被当作GC Roots,原因在于,作为一个GC Roots首先自身不能被回收,GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被GC所管理,因而选择这些区域内的对象作为GC roots,被GC roots引用的对象不被GC回收。

    是不是被判定无法通过GC Roots抵达的对象一定会被回收?
    答案是否定的,被判定不可达的对象,会被标记一次,然后会进行一次筛选,筛选的条件是看对象是否有必要执行finalize()方法,当finalize()没有被重写,或者已经执行过了的情况下,都会被视作没必要执行。

    如果有必要执行finalize(),那么对象就会被放在名为F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。如果一个对象finalize()方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。

    这样说可能有点迷,我用我拙劣的画技绘制了一个过程图。


    上图表示的很清晰,在F-Queue里,会有专门的线程去帮助对象逃逸,如果逃逸失败,只能是被回收的下场。

    而且一个对象的finalize只能被执行一次,一次自救失败,意味着不可再重生。

    参考资料
    周志明《深入理解Java虚拟机》
    https://blog.csdn.net/u010744711/article/details/51371535
    https://blog.csdn.net/luzhensmart/article/details/81431212

    相关文章

      网友评论

          本文标题:如何判断对象已死?

          本文链接:https://www.haomeiwen.com/subject/uslabqtx.html