)
在JVM的垃圾回收机制中对象存活判定和垃圾标记算法是非常重要的基础内容。准确地判定对象是否存活以及选择合适的垃圾标记算法对于高效地进行垃圾回收至关重要。接下来我们将深入探讨对象存活判定的方法以及常见的垃圾标记算法并通过Java代码进行演示。目录对象存活判定的方法引用计数法可达性分析算法标记算法标记 - 清除算法标记 - 整理算法总结对象存活判定的方法引用计数法引用计数法是一种比较简单的对象存活判定方法。它的核心思想是给每个对象添加一个引用计数器每当有一个地方引用该对象时计数器的值就加1当引用失效时计数器的值就减1。当计数器的值为0时就认为该对象不再被使用是一个垃圾对象可以被回收。用大白话来说就好比一个物品每有一个人使用它它的“使用人数”就加1当没有人再使用它时“使用人数”为0这个物品就可以被扔掉了。不过引用计数法存在一个明显的问题就是无法解决循环引用的问题。例如对象A引用了对象B对象B又引用了对象A此时它们的引用计数器都不为0但实际上它们可能已经没有其他外部引用应该被回收。以下是一个简单的Java代码示例展示引用计数法的基本原理classMyObject{MyObjectreference;}publicclassReferenceCountingExample{publicstaticvoidmain(String[]args){MyObjectobjAnewMyObject();MyObjectobjBnewMyObject();// 相互引用objA.referenceobjB;objB.referenceobjA;// 此时objA和objB的引用计数器都不为0但它们已经没有其他外部引用}}可达性分析算法可达性分析算法是目前主流的对象存活判定方法。它的基本思路是从一系列被称为“GC Roots”的对象开始通过引用关系向下搜索能够被搜索到的对象就是存活对象而那些无法被搜索到的对象则被判定为垃圾对象可以被回收。通俗来讲就像从一个树根开始沿着树枝去寻找树叶如果一片树叶和树根之间有树枝相连那么这片树叶就是“存活”的否则就是“死亡”的可以被摘掉。常见的GC Roots对象包括虚拟机栈栈帧中的本地变量表中引用的对象。例如在一个方法中创建的对象如果该方法还没有执行完那么这个对象就被虚拟机栈中的本地变量引用是存活对象。方法区中类静态属性引用的对象。比如一个类的静态成员变量引用的对象只要这个类还存在于方法区该对象就是存活的。方法区中常量引用的对象。例如使用final关键字定义的常量所引用的对象。本地方法栈中JNIJava Native Interface引用的对象。以下是一个简单的Java代码示例展示可达性分析算法的基本原理publicclassReachabilityAnalysisExample{publicstaticvoidmain(String[]args){// 创建一个对象ObjectobjnewObject();// obj是一个GC Roots可达的对象所以是存活对象// 让obj不再引用该对象objnull;// 此时该对象无法从GC Roots到达可能会被垃圾回收}}标记算法标记 - 清除算法标记 - 清除算法是一种基本的垃圾标记算法它分为两个阶段标记阶段和清除阶段。在标记阶段从GC Roots开始遍历标记所有存活的对象在清除阶段将那些没有被标记的对象即垃圾对象进行清除。用大白话解释就好比在一片森林里先把那些还“活着”的树做上标记然后把没有标记的树砍掉。不过标记 - 清除算法存在一些缺点。首先它会产生大量的内存碎片就像砍树后留下了很多小块的空地可能导致后续无法分配较大的连续内存空间。其次标记和清除的效率都不高尤其是在对象数量较多的情况下。以下是一个简单的Java代码示例模拟标记 - 清除算法的执行过程importjava.util.ArrayList;importjava.util.List;classMyObject{intid;publicMyObject(intid){this.idid;}}publicclassMarkAndSweepExample{publicstaticvoidmain(String[]args){ListMyObjectobjectsnewArrayList();// 创建一些对象for(inti0;i10;i){objects.add(newMyObject(i));}// 模拟标记阶段ListMyObjectmarkedObjectsnewArrayList();for(MyObjectobj:objects){if(obj.id%20){markedObjects.add(obj);}}// 模拟清除阶段objects.removeIf(obj-!markedObjects.contains(obj));// 输出存活的对象for(MyObjectobj:objects){System.out.println(存活对象的ID: obj.id);}}}标记 - 整理算法标记 - 整理算法也是先进行标记阶段从GC Roots开始遍历标记所有存活的对象。然后在整理阶段将所有存活的对象向内存的一端移动然后直接清理掉边界以外的内存空间。这就好比在一个房间里先把有用的物品标记出来然后把这些有用的物品都挪到房间的一边最后把另一边的空间清理干净。标记 - 整理算法解决了标记 - 清除算法产生内存碎片的问题因为它会对存活对象进行整理使得内存空间更加连续。但它的缺点是整理过程需要移动对象会带来一定的性能开销。以下是一个简单的Java代码示例模拟标记 - 整理算法的执行过程importjava.util.ArrayList;importjava.util.List;classMyObject{intid;publicMyObject(intid){this.idid;}}publicclassMarkAndCompactExample{publicstaticvoidmain(String[]args){ListMyObjectobjectsnewArrayList();// 创建一些对象for(inti0;i10;i){objects.add(newMyObject(i));}// 模拟标记阶段ListMyObjectmarkedObjectsnewArrayList();for(MyObjectobj:objects){if(obj.id%20){markedObjects.add(obj);}}// 模拟整理阶段objects.clear();objects.addAll(markedObjects);// 输出存活的对象for(MyObjectobj:objects){System.out.println(存活对象的ID: obj.id);}}}总结通过学习对象存活判定的方法和常见的垃圾标记算法我们能够更好地理解JVM的垃圾回收机制。引用计数法简单但存在循环引用问题可达性分析算法是主流的对象存活判定方法。标记 - 清除算法会产生内存碎片标记 - 整理算法则解决了这个问题但会带来一定的性能开销。在实际应用中我们需要根据具体的场景和需求选择合适的垃圾标记算法进行垃圾回收。掌握了对象存活判定和垃圾标记算法的内容后下一节我们将深入学习垃圾回收算法的具体实现进一步完善对本章JVM垃圾回收机制主题的认知。 系列专栏导航 专栏导航 深入理解JVM JDK5新特性 JDK8新特性 JDK11新特性 JDK17新特性 JDK21新特性建议按系列顺序阅读从基础到进阶逐步掌握核心能力避免遗漏关键知识点其他专栏衔接 《若依框架全攻略从入门到项目实战》 《深入浅出Mybatis》 全面掌握MySQL工具 《深入浅出Maven》 《深入浅出git》 《深入浅出Kafka》 《全面掌握Swagger从入门到实战》 《Lombok高效Java开发的秘密武器完全解读》 博客概览《程序员技术成长导航专栏汇总》全景导航博文系列《一口气学完fastJson》《一文搞懂PageHelper》《一文搞懂MyBatis》