发布于2026-05-23 阅读(0)
扫一扫,手机访问
在Ja va的世界里,一个对象何时该被回收,并非由程序员说了算,也不是看它有没有被“遗忘”。虚拟机自有一套严谨的判定逻辑,这套逻辑的核心,就是可达性分析算法。简单来说,它不关心对象被引用了多少次,只关心一件事:这个对象,还能不能从一组“根”对象出发,被找到?

上图清晰地展示了这个过程:从一组确定的起点(GC Roots)出发,沿着对象间的引用关系(引用链)进行搜索。所有能被“触达”的对象,都被标记为存活;而那些孤悬在引用网络之外的、无法从任何根节点抵达的对象,就会被判定为“死亡”,成为待回收的垃圾。这就好比在一片森林里,从几个固定的地标出发,所有能通过小路连通到的区域都是“可达”的,而那些完全隔绝、无路可通的区域,自然就被划为了可清理的荒地。
那么,什么样的对象有资格成为这片森林的“地标”——GC Roots呢?它们必须是JVM在运行时能明确识别、且在任何情况下都“绝对安全”、不会被回收的对象。通常包括以下几类:
static字段)所引用的对象。这些属于类级别的数据,生命周期通常很长。final static的包装类对象。这些根对象构成了搜索的绝对起点,是判定存活对象的基石。
引用链,其实就是对象之间手拉手形成的可达路径。它并非必须是直接引用,而是可以逐级传递。
举个例子:假设对象A是GC Root,它引用了对象B,对象B又引用了对象C,而对象C最后引用了对象D。那么,尽管A和D之间隔了两层,但只要这条链上的每一个引用都是有效的(没有被置为null,也没有被其他引用覆盖),从A出发,D就是可达的,因此D也是存活对象。
关键在于链条的完整性。一旦中间某个环节断裂——比如将B的引用设置为null——那么从B往后的整个子图(包括C和D),即使它们之间还相互引用,也因为失去了与根节点的连接,变成了不可达的“孤岛”,从而被标记为可回收。
提到对象存活判定,很多人会想到更直观的引用计数法:每个对象带一个计数器,被引用一次就加一,引用失效就减一,归零即回收。听起来简单明了,对吧?
但这个方法有个致命的缺陷:它无法处理循环引用。想象一下,对象A和对象B互相引用,除此之外,再也没有任何外部引用指向它们。此时,A和B的引用计数都为1,永远不为零。按照引用计数法的规则,它们永远不会被回收,但实际上,它们已经是程序无法访问的垃圾了。
可达性分析算法的高明之处,就在于它从“根”出发的搜索方式,天然地绕过了这个陷阱。循环引用的对象组,只要它们整体无法从任何GC Root到达,就会被一网打尽,判定为可回收,从而完美解决了循环引用导致的“内存泄漏”问题。
会。而且这是一个关键点。在枚举所有GC Roots(即找出所有根对象)的这个初始阶段,JVM必须让整个程序“暂停”一下,这个动作被称为STW(Stop-The-World)。
为什么要暂停?试想一下,如果在搜索根节点的过程中,程序还在不停地创建新对象、修改引用关系,那搜索结果就会错乱,可能导致该回收的没回收,不该回收的却被误杀。因此,为了保证分析结果的准确性,JVM需要让所有用户线程都到达一个安全点(SafePoint)并停下来,等根节点枚举这个“快照”拍完了,再恢复运行。
值得注意的是,这个STW的时间通常非常短暂。而且,在现代先进的垃圾收集器(如G1、ZGC)中,它们采用了更精巧的设计:仅在根节点枚举阶段进行短暂的STW,后续的标记过程(即沿着引用链遍历所有对象)是可以与用户程序并发执行的,从而极大地减少了停顿时间对应用的影响。
所以说,可达性分析不仅是JVM垃圾回收的理论基石,其实现细节(如短暂的STW)也深刻影响着应用程序的响应性能,是理解Ja va内存管理不可或缺的一环。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8