java 垃圾回收中引用计数法的主要缺点是存在循环引用导致内存泄漏,且每次对象操作都需要维护引用数带来性能开销,因此 java 虚拟机最终选择了基于可达性分析的标记-清除或复制算法而非引用计数。

为什么循环引用会导致内存泄漏
在引用计数算法中,每个对象都有一个引用计数器,当引用该对象的引用数变为 0 时,对象才会被回收。如果两个对象互相引用,即使它们已经不再被任何外部根对象(如栈帧中的局部变量)引用,它们的引用计数也永远不会为 0,从而导致无法回收的内存泄漏。

public class CycleRefExample {
Object other;
public static void main(String[] args) {
// 创建两个对象并互相引用
CycleRefExample objA = new CycleRefExample();
CycleRefExample objB = new CycleRefExample();
objA.other = objB;
objB.other = objA;
// 断开与外部根对象的连接
objA = null;
objB = null;
// 在引用计数算法下,objA和objB的引用计数仍为1,无法回收
// 但在Java的GC中,它们会被正确回收
System.gc();
}
}
引用计数带来的性能开销有哪些
引用计数算法需要在每次对象赋值、销毁或方法调用时增加或减少引用计数,这增加了 CPU 的负担。此外,引用计数的更新操作需要保证原子性,这在多线程环境下可能需要加锁或使用 CAS 操作,进一步降低了性能。相比之下,Java 使用的分代收集算法通过批量处理对象的生命周期管理,减少了频繁的计数更新操作。

public class PerformanceExample {
private int count = 0;
public void increment() {
// 模拟引用计数的频繁更新
count++; // 每次操作都需要更新计数
}
public static void main(String[] args) {
PerformanceExample example = new PerformanceExample();
long startTime = System.currentTimeMillis();
for (int i = 0; i
<h2>Java 如何选择替代方案解决这些问题</h2><p>Java 虚拟机采用了基于可达性分析的垃圾回收算法,通过从 GC Roots 开始遍历对象图,标记所有可达对象,然后回收不可达对象。这种方法避免了循环引用的问题,因为即使对象之间存在循环引用,只要它们不可达,就会被回收。同时,可达性分析可以在特定的时间点(如 Full GC 或 Minor GC)批量处理对象,减少了频繁的计数更新操作,提高了性能。</p>
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











