GC Roots根与判断对象死活算法

都说无用的对象会被gc,那怎么判断哪些是无用的对象,那些还是有用的对象?

引用计数法

  • 给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;当引用失效时,计数器就-1;任何时刻计数器为0的对象就是不能再被使用的,即对象已"死"
  • 引用计数法实现简单,判定效率也比较高,在大部分情况下都是一个不错的算法。比如Python语言就采用引用计
    数法进行内存管理。
  • 但是,在主流的JVM中没有选用引用计数法来管理内存,最主要的原因就是引用计数法无法解决对象的循环引用问题

可达性分析算法

GC Roots根

  • 类加载器
  • Thread
  • 虚拟机栈中的本地变量表
  • static成员
  • 常量引用
  • 本地方法栈的变量等等

可达性分析算法

此算法的核心思想为 : 通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径
称之为"引用链",当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达)时,证明此对象
是不可用的。

在这里插入图片描述
对象Object5-Object7之间虽然彼此还有关联,但是它们到GC Roots是不可达的,因此他们会被判定为可回收对

对象自我拯救

要宣告一个对象的真正死亡,至少要经历两次标记过程 : 如果对象在进行可达性分析之后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被JVM调用过,虚拟机会将这两种情况都视为"没有必要执行",此时的对象才是真正"死"的对象 .

public class Test {
    public static Test test;
    public void isAlive() {
        System.out.println("I am alive :)");
    } 
    
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
        test = this;
    } 
    
    public static void main(String[] args)throws Exception {
        test = new Test();
        test = null;
        System.gc();
        Thread.sleep(500);
        if (test != null) {
            test.isAlive();
        }else {
            System.out.println("no,I am dead :(");
        } // 下面代码与上面完全一致,但是此次自救失败
        test = null;
        System.gc();
        Thread.sleep(500);
        if (test != null) {
            test.isAlive();
        }else {
            System.out.println("no,I am dead :(");
        }
    }
} 
  • 从上面代码示例我们发现,finalize方法确实被JVM触发,并且对象在被收集前成功逃脱。
  • 但是从结果上我们发现,两个完全一样的代码片段,结果是一次逃脱成功,一次失败。这是因为,任何一个对象的finalize()方法都只会被系统自动调用一次,如果相同的对象在逃脱一次后又面临一次回收,它的finalize()方法不被再次执行,因此第二段代码的自救行动失败。
发布了48 篇原创文章 · 获赞 14 · 访问量 7386

猜你喜欢

转载自blog.csdn.net/weixin_43508555/article/details/104638386
今日推荐