55.垃圾标记阶段-引用计数算法、可达性分析算法

目录

  • 1.引用计数算法
  • 2.可达性分析算法

垃圾标记阶段算法包括引用计数算法和可达性分析算法。

  1. 在进行垃圾回收之前,需要判断哪些对象是存活对象,哪些是死亡对象,只有被标记为死亡的对象才能够被回收。
  2. 当一个对象已经不再被任何的存活对象继续引用的时候,就可以宣判为已经死亡。
  3. 判断对象是否存活一般有两种方式:引用计数算法和可达性分析算法。
    55.垃圾标记阶段-引用计数算法、可达性分析算法_第1张图片

1.引用计数算法

  1. 引用计数算法,会对每个对象都保存一个整型的引用计数器属性,用于记录对象被引用的情况。
  2. 对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1;当引用失效的时候,对象A的引用计数器就减1。只要引用计数器的值为0,则表示对象A不可能再被使用,可以被回收。
    优点:实现简单、垃圾便于辨识;判定效率高,回收没有延迟。
    缺点:需要单独的字段存储计数器,额外的存储空间开销;需要更新计数器,伴随着加法和减法操作,带来时间开销;无法处理循环引用的情况。

由于无法处理循环引用的情况,所以JVM没有使用这种算法。
55.垃圾标记阶段-引用计数算法、可达性分析算法_第2张图片
循环引用例子:

public class RefCountGC {
    //这个成员属性唯一的作用就是占用一点内存
    private byte[] bigSize = new byte[5 * 1024 * 1024];//5MB

    Object reference = null;

    public static void main(String[] args) {
        RefCountGC obj1 = new RefCountGC();
        RefCountGC obj2 = new RefCountGC();

        obj1.reference = obj2;
        obj2.reference = obj1;

        obj1 = null;
        obj2 = null;
        //显式的执行垃圾回收行为
        //这里发生GC,obj1和obj2能否被回收? 能被回收,是因为JVM采用的不是引用计数算法。所以obj1和obj2能被回收。这里反向证明了JVM没有采用引用计数算法。
        System.gc();

        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

上面的代码就存在循环引用的情况,将obj1obj2设为null之后,堆区中的对象仍然会保持互相引用的情况,如果使用引用计数的方式就无法回收,因为对象obj1obj2中的计数器都是1,是不能被回收的。
55.垃圾标记阶段-引用计数算法、可达性分析算法_第3张图片

2.可达性分析算法

  1. 别名:根搜索算法、追踪性垃圾收集(Tracing Garbage Collection)
  2. 优点:实现简单,执行高效。而且能够解决循环引用的问题。
  3. JavaC#都选择这种算法。
    55.垃圾标记阶段-引用计数算法、可达性分析算法_第4张图片
  4. 从根对象集合(GC Roots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。
  5. 使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索所走过的路径称为引用链(Reference Chain)
  6. 如果对象从根对象集合出发不可达,则被标记为垃圾对象;否则是存活对象
    55.垃圾标记阶段-引用计数算法、可达性分析算法_第5张图片
    可达性分析算法:
    如下图所示,从GC Roots出发,从上至下能够到达的对象,都是存活对象(蓝色);灰色的对象是可回收对象,从GC Roots出发,不可达。
    55.垃圾标记阶段-引用计数算法、可达性分析算法_第6张图片

GC Roots可以是哪些元素?(面试常问)
要能够回答出前四个:

  1. 虚拟机栈中对象的引用
  2. 本地方法栈中JNI对象的引用
  3. 方法区中类静态属性(类中的static变量)
  4. 方法区中常量对象的引用(字符串常量池的引用)
    。。。
    55.垃圾标记阶段-引用计数算法、可达性分析算法_第7张图片
    简单的判断方法如果一个指针,指向了堆内存里面的对象,但是自己又不存放在堆里面,那它就是一个Root
    55.垃圾标记阶段-引用计数算法、可达性分析算法_第8张图片
    小技巧:如下图所示,在堆区外面的栈区、本地方法栈中指向堆区对象的引用;方法区中对堆区静态变量以及字符串常量的引用都是Root。注意静态变量和字符串常量池放到了堆区。
    概括的说就是:就是堆区外面的部分,只要是指向堆区内部的对象或者内容的变量,就都是Root。
    55.垃圾标记阶段-引用计数算法、可达性分析算法_第9张图片
    注意:要使用可达性分析算法判断内存是否可回收,那么分析工作必须在一个能保障一致性的快照中进行。为了保持一致性,GC的时候必须Stop The World
    55.垃圾标记阶段-引用计数算法、可达性分析算法_第10张图片

更多JVM文章请访问我的JVM专栏:
https://blog.csdn.net/u011069294/category_10113093.html

你可能感兴趣的:(JVM,引用计数算法,可达性分析算法,GC,Roots)