You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中GC(垃圾回收)与内存泄漏:如何检测循环引用对象的可回收性

Java中循环引用对象的GC回收问题解答

结论

voidTest方法执行完毕后,内部创建的person和room对象会被GC回收,即使它们存在循环引用。

原理解释

Java的垃圾回收采用可达性分析算法,核心逻辑是:以GC Roots(栈帧局部变量、静态变量、常量引用、JNI引用等)为起点,遍历所有对象的引用链,只要对象不在任何一条从GC Roots出发的引用链上,就会被标记为可回收对象。

在你的代码中:

  • 当voidTest执行完毕,方法对应的栈帧会从Java虚拟机栈中弹出,栈帧里的person和room局部变量(属于GC Roots的一部分)会被销毁。
  • 堆内存中的Person和Room对象虽然互相引用,但已经没有任何GC Roots能到达它们,因此属于不可达对象,满足GC回收条件。

这里需要注意:循环引用只会影响引用计数法的垃圾回收(比如Python早期的GC),但Java的可达性分析完全不受循环引用的影响。

测试方法

方法1:重写finalize()方法观察回收日志

finalize()方法是对象被回收前会调用的方法(仅用于测试,不推荐生产代码使用),通过打印日志可以验证对象是否被回收:

class Main{
    public static void main(String[] args) {
        voidTest();
        // 手动触发GC(仅测试用,实际GC时机由JVM决定)
        System.gc();
        // 暂停线程,给GC执行时间
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void voidTest(){
        Person person = new Person();
        Room room = new Room();

        room.person = person;
        person.room = room; // 修正原代码拼写错误:roon -> room
    }
}

class Room{
    public int id;
    public Person person;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("Room对象已被GC回收");
    }
}

class Person{
    public String name;
    public Room room;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("Person对象已被GC回收");
    }
}

运行代码后,如果控制台输出两个回收日志,说明对象已被成功回收。

方法2:使用WeakReference和ReferenceQueue验证

弱引用(WeakReference)的特点是:只要GC发现弱引用指向的对象没有强引用存在,就会回收该对象,并将弱引用放入关联的引用队列中。通过检查引用队列可以判断对象是否被回收:

import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;

class Main{
    public static void main(String[] args) {
        ReferenceQueue<Person> personQueue = new ReferenceQueue<>();
        ReferenceQueue<Room> roomQueue = new ReferenceQueue<>();
        
        voidTest(personQueue, roomQueue);
        
        // 手动触发GC
        System.gc();
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // 检查引用队列,若不为空说明对象已被回收
        System.out.println("Person对象是否被回收:" + (personQueue.poll() != null));
        System.out.println("Room对象是否被回收:" + (roomQueue.poll() != null));
    }

    public static void voidTest(ReferenceQueue<Person> pQueue, ReferenceQueue<Room> rQueue){
        Person person = new Person();
        Room room = new Room();

        // 用弱引用包装对象并关联引用队列
        WeakReference<Person> personRef = new WeakReference<>(person, pQueue);
        WeakReference<Room> roomRef = new WeakReference<>(room, rQueue);
        
        room.person = person;
        person.room = room;
        
        // 移除局部变量的强引用
        person = null;
        room = null;
    }
}

class Room{
    public int id;
    public Person person;
}

class Person{
    public String name;
    public Room room;
}

运行代码后,如果输出true,说明对应对象已被GC回收。

内容的提问来源于stack exchange,提问作者Kien Truong Quang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 07:05:19