Skip to content

什么是三色标记

三色标记法是一种用于垃圾回收算法中的对象标记方法,特别用于标记-清除型垃圾回收器。这种方法通过使用三种颜色(白色、灰色和黑色)来跟踪对象的可达性和垃圾回收状态,以避免对象的重复回收和丢失。

三色标记的基本概念

  • 白色:表示对象尚未被检查。白色对象可能是垃圾,直到证明它们是可达的。
  • 灰色:表示对象被检查过,并且其本身是可达的,但其引用的对象还未全部检查。
  • 黑色:表示对象和它所有引用的对象都已检查且是可达的。

三色标记步骤

  1. 初始化:所有对象开始时都是白色。
  2. 标记开始:从GC Roots开始,根对象标记为灰色。
  3. 扫描灰色对象
    • 将灰色对象引用的所有白色对象标记为灰色。
    • 然后将该灰色对象标记为黑色。
  4. 重复步骤3直到没有更多的灰色对象。
  5. 清除:未标记为黑色的对象为白色,即垃圾,可被回收。

代码示例模拟

以下是一个简单的模拟示例,展示了假设某个对象图的三色标记流程:

java
import java.util.HashSet;  
import java.util.Set;  

class ObjectNode {  
    String name;  
    Set<ObjectNode> references = new HashSet<>();  
    String color = "White"; // White by default  

    ObjectNode(String name) {  
        this.name = name;  
    }  

    void addReference(ObjectNode node) {  
        references.add(node);  
    }  

    @Override  
    public String toString() {  
        return name;  
    }  
}  

public class TricolorMarkingExample {  
    private static Set<ObjectNode> roots = new HashSet<>();  

    public static void main(String[] args) {  
        // Create objects  
        ObjectNode objA = new ObjectNode("A");  
        ObjectNode objB = new ObjectNode("B");  
        ObjectNode objC = new ObjectNode("C");  

        // Create references  
        objA.addReference(objB);  
        objB.addReference(objC);  

        // GC Roots  
        roots.add(objA);   
        roots.add(objB);  

        // Perform tricolor marking  
        performTricolorMarking();  
    }  

    static void performTricolorMarking() {  
        // Initial marking of root nodes  
        for (ObjectNode root : roots) {  
            markGray(root);  
        }  

        while (roots.stream().anyMatch(node -> node.color.equals("Gray"))) {  
            for (ObjectNode root : roots) {  
                if (root.color.equals("Gray")) {  
                    scanAndBlacken(root);  
                }  
            }  
        }  

        // Output the color result  
        System.out.println("Final color states:");  
        roots.forEach(root -> System.out.println(root.name + ": " + root.color + ", references: " + root.references));  
    }  

    static void markGray(ObjectNode node) {  
        if (node.color.equals("White")) {  
            System.out.println("Marking " + node.name + " gray.");  
            node.color = "Gray";  
        }  
    }  

    static void scanAndBlacken(ObjectNode node) {  
        System.out.println("Scanning and blackening " + node.name);  
        node.references.forEach(reference -> {  
            if (reference.color.equals("White")) {  
                markGray(reference);  
            }  
        });  
        node.color = "Black";  
    }  
}

解释

  • 对象生成:创建若干对象并引用它们。
  • 根集合:设定起始对象集,通常指代程序运行时栈、全局引用等。
  • 三色规则应用:通过markGrayscanAndBlacken函数演示从GC Roots开始的标记过程。
  • 最终输出:显示对象的颜色状态和引用关系,以验证三色标记的正确性。

使用三色标记的好处

  • 避免循环引用:能够正确识别循环引用对象不被误判为不可回收。
  • 并发性增强:支持并发标记,减少“Stop-the-World”时间。
  • 渐进性:可以执行增量垃圾回收,减小程序暂停。

在实际的垃圾回收算法实现中,如G1和CMS,三色标记法被广泛应用,用于高效地管理内存和提高程序运行的性能。

更新: 2024-08-09 15:59:48
原文: https://www.yuque.com/tulingzhouyu/db22bv/sguahv8u1s2tckek

短视频

最近有一位工作 3 年的粉丝,在后台留言 面试的时候被问到什么是三色标记法?当时一脸懵逼,天天 CRUD,三色标记是什么鬼啊。

其实三色标记法是Java虚拟机中一种用于垃圾回收的算法,主要是用于标记内存中的对象,好确定哪些对象是存活的,哪些对象是可以被回收的。

这种算法将内存中的对象分为三种颜色白色 黑色 灰色

白色代表对象还没被扫描过,我们可以理解为是未知状态

黑色代表对象已经被扫描过,但是 它的引用对象还没被有扫描过

灰色代表对象已经被扫描过了, 并且它的引用对象也全都被扫描过了,我们可以理解为是已知状态

在GC开始的时候会将所有对象都标记为白色。

然后从根对象开始遍历内存中的对象,遍历过程中它会把直接引用对象都标记为灰色,并且把它们加入到灰色集合中。

接着呢,会逐个判断灰色集合中的对象是否存在子对象

如果存在就继续放入灰色集合中,如果不存在就标记为黑色,放入到黑色集合中。

然后重复这个过程,直到灰色集合处理为空,也就代表这一轮标记就完成了。

最终,所有存活的对象都会被标记为黑色,而未被标记的白色对象就是待回收的垃圾对象。

而垃圾回收器通过三色标记算法 在一定程度上减少了** gc 停顿时间**,提高了系统的性能和响应速度

以上就是我对三色标记法的理解。

更新: 2024-05-15 14:13:11
原文: https://www.yuque.com/tulingzhouyu/db22bv/xnnlrqaa80pnu0zn