在JVM的垃圾回收机制中,对象存活判定和垃圾标记算法是非常重要的基础内容。准确地判定对象是否存活,以及选择合适的垃圾标记算法,对于高效地进行垃圾回收至关重要。接下来,我们将深入探讨对象存活判定的方法以及常见的垃圾标记算法,并通过Java代码进行演示。

对象存活判定的方法

引用计数法

引用计数法是一种比较简单的对象存活判定方法。它的核心思想是,给每个对象添加一个引用计数器,每当有一个地方引用该对象时,计数器的值就加1;当引用失效时,计数器的值就减1。当计数器的值为0时,就认为该对象不再被使用,是一个垃圾对象,可以被回收。

用大白话来说,就好比一个物品,每有一个人使用它,它的“使用人数”就加1,当没有人再使用它时,“使用人数”为0,这个物品就可以被扔掉了。

不过,引用计数法存在一个明显的问题,就是无法解决循环引用的问题。例如,对象A引用了对象B,对象B又引用了对象A,此时它们的引用计数器都不为0,但实际上它们可能已经没有其他外部引用,应该被回收。

以下是一个简单的Java代码示例,展示引用计数法的基本原理:

class MyObject {
    MyObject reference;
}

public class ReferenceCountingExample {
    public static void main(String[] args) {
        MyObject objA = new MyObject();
        MyObject objB = new MyObject();

        // 相互引用
        objA.reference = objB;
        objB.reference = objA;

        // 此时objA和objB的引用计数器都不为0,但它们已经没有其他外部引用
    }
}
可达性分析算法

可达性分析算法是目前主流的对象存活判定方法。它的基本思路是,从一系列被称为“GC Roots”的对象开始,通过引用关系向下搜索,能够被搜索到的对象就是存活对象,而那些无法被搜索到的对象则被判定为垃圾对象,可以被回收。

通俗来讲,就像从一个树根开始,沿着树枝去寻找树叶,如果一片树叶和树根之间有树枝相连,那么这片树叶就是“存活”的,否则就是“死亡”的,可以被摘掉。

常见的GC Roots对象包括:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。例如,在一个方法中创建的对象,如果该方法还没有执行完,那么这个对象就被虚拟机栈中的本地变量引用,是存活对象。
  • 方法区中类静态属性引用的对象。比如,一个类的静态成员变量引用的对象,只要这个类还存在于方法区,该对象就是存活的。
  • 方法区中常量引用的对象。例如,使用final关键字定义的常量所引用的对象。
  • 本地方法栈中JNI(Java Native Interface)引用的对象。

以下是一个简单的Java代码示例,展示可达性分析算法的基本原理:

public class ReachabilityAnalysisExample {
    public static void main(String[] args) {
        // 创建一个对象
        Object obj = new Object();

        // obj是一个GC Roots可达的对象,所以是存活对象

        // 让obj不再引用该对象
        obj = null;

        // 此时该对象无法从GC Roots到达,可能会被垃圾回收
    }
}

标记算法

标记 - 清除算法

标记 - 清除算法是一种基本的垃圾标记算法,它分为两个阶段:标记阶段和清除阶段。在标记阶段,从GC Roots开始遍历,标记所有存活的对象;在清除阶段,将那些没有被标记的对象(即垃圾对象)进行清除。

用大白话解释,就好比在一片森林里,先把那些还“活着”的树做上标记,然后把没有标记的树砍掉。

不过,标记 - 清除算法存在一些缺点。首先,它会产生大量的内存碎片,就像砍树后留下了很多小块的空地,可能导致后续无法分配较大的连续内存空间。其次,标记和清除的效率都不高,尤其是在对象数量较多的情况下。

以下是一个简单的Java代码示例,模拟标记 - 清除算法的执行过程:

import java.util.ArrayList;
import java.util.List;

class MyObject {
    int id;

    public MyObject(int id) {
        this.id = id;
    }
}

public class MarkAndSweepExample {
    public static void main(String[] args) {
        List<MyObject> objects = new ArrayList<>();

        // 创建一些对象
        for (int i = 0; i < 10; i++) {
            objects.add(new MyObject(i));
        }

        // 模拟标记阶段
        List<MyObject> markedObjects = new ArrayList<>();
        for (MyObject obj : objects) {
            if (obj.id % 2 == 0) {
                markedObjects.add(obj);
            }
        }

        // 模拟清除阶段
        objects.removeIf(obj -> !markedObjects.contains(obj));

        // 输出存活的对象
        for (MyObject obj : objects) {
            System.out.println("存活对象的ID: " + obj.id);
        }
    }
}
标记 - 整理算法

标记 - 整理算法也是先进行标记阶段,从GC Roots开始遍历,标记所有存活的对象。然后,在整理阶段,将所有存活的对象向内存的一端移动,然后直接清理掉边界以外的内存空间。

这就好比在一个房间里,先把有用的物品标记出来,然后把这些有用的物品都挪到房间的一边,最后把另一边的空间清理干净。

标记 - 整理算法解决了标记 - 清除算法产生内存碎片的问题,因为它会对存活对象进行整理,使得内存空间更加连续。但它的缺点是整理过程需要移动对象,会带来一定的性能开销。

以下是一个简单的Java代码示例,模拟标记 - 整理算法的执行过程:

import java.util.ArrayList;
import java.util.List;

class MyObject {
    int id;

    public MyObject(int id) {
        this.id = id;
    }
}

public class MarkAndCompactExample {
    public static void main(String[] args) {
        List<MyObject> objects = new ArrayList<>();

        // 创建一些对象
        for (int i = 0; i < 10; i++) {
            objects.add(new MyObject(i));
        }

        // 模拟标记阶段
        List<MyObject> markedObjects = new ArrayList<>();
        for (MyObject obj : objects) {
            if (obj.id % 2 == 0) {
                markedObjects.add(obj);
            }
        }

        // 模拟整理阶段
        objects.clear();
        objects.addAll(markedObjects);

        // 输出存活的对象
        for (MyObject obj : objects) {
            System.out.println("存活对象的ID: " + obj.id);
        }
    }
}

总结

通过学习对象存活判定的方法和常见的垃圾标记算法,我们能够更好地理解JVM的垃圾回收机制。引用计数法简单但存在循环引用问题,可达性分析算法是主流的对象存活判定方法。标记 - 清除算法会产生内存碎片,标记 - 整理算法则解决了这个问题,但会带来一定的性能开销。在实际应用中,我们需要根据具体的场景和需求,选择合适的垃圾标记算法进行垃圾回收。

掌握了对象存活判定和垃圾标记算法的内容后,下一节我们将深入学习垃圾回收算法的具体实现,进一步完善对本章JVM垃圾回收机制主题的认知。


🍃 系列专栏导航


建议按系列顺序阅读,从基础到进阶逐步掌握核心能力,避免遗漏关键知识点~

其他专栏衔接

全景导航博文系列

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐