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

为何PhantomReference.get()始终返回null?设计逻辑探究

为何PhantomReference的get()方法始终返回null?

我熟悉Java中的各类引用、可达性概念及垃圾回收机制,也在个人项目中使用过不同类型的Reference,但有一个设计选择令我困惑:为何PhantomReference的get()方法始终返回null?

PhantomReference主要用于对象“消亡”后的清理操作,常见解释是“确保可回收对象保持可回收状态,避免清理启动后对象重新变为强可达”,正如官方文档所述:

为确保可回收对象保持该状态,无法获取虚引用的引用对象:虚引用的get方法始终返回null。

但我认为,“确保可回收对象保持可回收”并不意味着get()必须始终返回null:只要PhantomReference未被清除,其引用对象就不可回收,也不会入队,清理操作尚未启动——此时get()可以安全返回引用对象。只有当引用被清除后,对象才会变为可回收,此时get()自然返回null。且不存在竞态条件,因为文档说明所有清除操作都是原子性的:

此时会原子性地清除指向该对象的所有虚引用,以及指向该对象可到达的其他虚可达对象的所有虚引用。

我提出该问题并非为解决具体业务问题,而是为在硕士论文中准确描述各类Reference类型的设计逻辑。不过我想到一个WeakReference无法覆盖的场景:

假设编写一个提供单例服务(PublicSingletonService)的库,该服务占用大量资源,需在无人使用时回收,再次请求时重建。库通过WeakReference持有单例,用ReferenceQueue检测回收并清理。但应用可通过带终结器的holder对象复活单例,导致库创建新实例,最终出现两个单例。

若同时使用PhantomReference和WeakReference,会出现WeakReference已清除但PhantomReference未清除的情况,此时库既无法访问旧实例也无法创建新实例。若PhantomReference.get()不始终返回null,直接用PhantomReference替代WeakReference即可解决该问题。以下是示例代码:

package weirdjava;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.Semaphore;

import weirdjava.StronglyReachableAfterWeakReferenceCleared.Library.PublicSingletonService;

public class StronglyReachableAfterWeakReferenceCleared
{
    public static void main(String[] args) throws InterruptedException
    {
        Application.run();
    }

    public static class Application
    {
        public static void run() throws InterruptedException
        {
            // 应用代码初始化库
            Library library = new Library();
            // 库创建公共服务的单例,并尝试检测它何时完全不可达
            PublicSingletonService singleton = library.getSingleton();

            // 应用代码对单例执行一些非常规操作(恶意行为、糟糕编程或复杂交互):
            // 创建holder
            ApplicationHolder holder = new ApplicationHolder(singleton);
            // 清除指向单例的最后一个强引用
            singleton = null;
            // 确保库的弱引用被实际清除 - 模拟应用占用大量内存的场景
            System.gc();

            // 库尝试检测对象是否已被清除,并在必要时执行清理
            // 在这个示例中,我们确保对象已被清除
            library.doCleanupIfNecessary();

            // 应用再次向库请求单例 - 因为旧实例已被清除,库会创建新实例
            PublicSingletonService secondSingleton = library.getSingleton();

            // 现在,应用神奇地复活了第一个单例
            PublicSingletonService resurrectedSingleton = holder.getReferent();
            if(resurrectedSingleton == null)
                throw new IllegalStateException("示例执行失败");

            System.out.println("复活成功!复活的实例:" + resurrectedSingleton);

            // 此时,指向对象的弱引用已被清除,
            // 但对象仍处于强可达状态 -
            // 且库的类并没有任何终结器
            System.out.println("应用现在拥有两个单例:" + resurrectedSingleton + ", " + secondSingleton);
        }
        private static class ApplicationHolder
        {
            private final Semaphore     referrerFinalizationComplete;
            private FinalizableReferrer referrerAfterFinalization;

            public ApplicationHolder(PublicSingletonService referent)
            {
                referrerFinalizationComplete = new Semaphore(0);
                // 显式不保留对该对象的引用,使其能够被回收
                new FinalizableReferrer(this, referent);
            }

            public PublicSingletonService getReferent() throws InterruptedException
            {
                referrerFinalizationComplete.acquire();
                return referrerAfterFinalization.getReferent();
            }

            private static class FinalizableReferrer
            {
                private final ApplicationHolder referrer;
                private final PublicSingletonService    referent;

                public FinalizableReferrer(ApplicationHolder referrer, PublicSingletonService referent)
                {
                    this.referrer = referrer;
                    this.referent = referent;
                }

                @Override
                protected void finalize()
                {
                    System.out.println("终结器使引用对象再次可达");
                    referrer.referrerAfterFinalization = this;
                    referrer.referrerFinalizationComplete.release();
                }

                public PublicSingletonService getReferent()
                {
                    return referent;
                }
            }
        }
    }

    public static class Library
    {
        // 仅用于演示;实际场景中需要保证线程安全等
        private int instanceCounter = 0;

        private final ReferenceQueue<PublicSingletonService>    refqueue;
        private WeakReference<PublicSingletonService>       publicSingletonObjectRef;

        public Library()
        {
            this.refqueue = new ReferenceQueue<>();
        }

        public void doCleanupIfNecessary() throws InterruptedException
        {
            // 实际中应该用poll()而非remove() - 但在这个示例中,
            // 即使调用了System.gc(),入队操作也不够快
            Reference<? extends PublicSingletonService> poll = refqueue.remove();
            if(poll != null)
            {
                System.out.println("引用对象已被清除;weakRef.get()返回:" + publicSingletonObjectRef.get()); // 返回null
                // 库执行必要的清理操作
                System.out.println("库执行清理");
            } else
                // 实际中库会什么都不做
                throw new IllegalStateException("示例执行失败");
        }

        public PublicSingletonService getSingleton()
        {
            if(publicSingletonObjectRef != null)
            {
                // 这个get()调用无法用PhantomReference实现
                PublicSingletonService previousInstance = publicSingletonObjectRef.get();
                if(previousInstance != null)
                    return previousInstance;
            }

            PublicSingletonService publicSingletonObjectInstance = new PublicSingletonService("实例 #" + instanceCounter ++);
            publicSingletonObjectRef = new WeakReference<>(publicSingletonObjectInstance, refqueue);
            return publicSingletonObjectInstance;
        }

        public static class PublicSingletonService
        {
            private final String value;

            public PublicSingletonService(String value)
            {
                this.value = value;
                System.out.println("创建单例对象:" + value);
            }

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

内容的提问来源于stack exchange,提问作者Haspamelodica

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 03:59:50