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

JVM中可存在多个ReferenceQueue吗?弱引用会入队哪个队列?

JVM中ReferenceQueue的多实例与弱引用入队规则

场景步骤

  • 主线程构造WeakReference并将其放入map中
  • 守护线程监控即将被GC回收的WeakReference
  • 使用WeakHashMap时,发现被回收的对象并未进入WeakReference构造方法中指定的队列,推测WeakHashMap内部也存在ReferenceQueue,对象会在put()方法中被清理。

核心问题

JVM中是否可以存在多个ReferenceQueue?弱引用会被入队到哪个ReferenceQueue?


验证代码1(HashMap场景)

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;


public class ReferenceQueueTest {
   
    private static ReferenceQueue<Byte[]> rq = new ReferenceQueue<>();

    private static int _1M = 1024 * 1024;

    public static void main(String[] args) {
        Object value = new Object();
        Map<Object, Object> hashMap =new HashMap<>();
        //Map<Object, Object> weakHashMap =new WeakHashMap<>();


        Thread thread = new Thread(() -> {
            try {
                int cnt = 1;
                WeakReference<Byte[]> k;
                while ((k = (WeakReference) rq.remove()) != null) {
                    System.out.println(cnt++ + "回收了:" + k);
                }
            }catch(InterruptedException e){
            }
        });
        thread.setDaemon(true);
        thread.start();

        for (int i = 0; i < 100; i++) {
            Byte[] bytes = new Byte[_1M];
            WeakReference<Byte[]> weakReference = new WeakReference<>(bytes, rq);
            hashMap.put(weakReference,value);
        }

        System.out.println("HashMap.size ->" + hashMap.size());

    }
}

控制台输出1

…………
55回收了:java.lang.ref.WeakReference@3834d63f
56回收了:java.lang.ref.WeakReference@3ab39c39
57回收了:java.lang.ref.WeakReference@7cdbc5d3
HashMap.size ->100

验证代码2(WeakHashMap场景)

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;


public class ReferenceQueueTest2 {
   
    private static ReferenceQueue<Byte[]> rq = new ReferenceQueue<>();

    private static int _1M = 1024 * 1024;

    public static void main(String[] args) {
        Object value = new Object();
        //Map<Object, Object> hashMap =new HashMap<>();
        Map<Object, Object> weakHashMap =new WeakHashMap<>();


        Thread thread = new Thread(() -> {
            try {
                int cnt = 1;
                WeakReference<Byte[]> k;
                while ((k = (WeakReference) rq.remove()) != null) {
                    System.out.println(cnt++ + "回收了:" + k);
                }
            }catch(InterruptedException e){
            }
        });
        thread.setDaemon(true);
        thread.start();

        for (int i = 0; i < 100; i++) {
            Byte[] bytes = new Byte[_1M];
            WeakReference<Byte[]> weakReference = new WeakReference<>(bytes, rq);
            //hashMap.put(weakReference,value);
            weakHashMap.put(weakReference,value);
        }
        System.out.println("weakHashMap.size ->" + weakHashMap.size());
    }
}

控制台输出2

weakHashMap.size ->40

问题解答

1. JVM支持多个ReferenceQueue实例

JVM允许开发者创建任意数量的ReferenceQueue实例,每个队列相互独立,各自负责接收关联到自身的引用对象。

2. 弱引用的入队目标由创建时指定

当通过WeakReference(T referent, ReferenceQueue<? super T> q)构造方法创建弱引用时,若指定了队列q,则当被引用对象referent被GC回收后,该WeakReference实例会被入队到指定的q队列;若创建时未指定队列,则引用对象被回收后不会进入任何队列。

结合验证代码的细节解释

  • 代码1(HashMap):自定义WeakReference关联了rq队列,当Byte[]被GC回收时,对应的WeakReference会被入队到rq,因此守护线程能打印回收日志。而HashMap通过强引用持有所有WeakReference实例,所以size保持100。
  • 代码2(WeakHashMap):WeakHashMap内部维护了私有ReferenceQueue。当我们将自定义的WeakReference作为键放入时,WeakHashMap会把这个WeakReference实例本身包装成内部弱引用并关联私有队列,而非使用我们指定的rq。当内存压力导致部分WeakReference实例(作为键)被回收时,WeakHashMap通过内部队列清理过期条目,我们的rq不会收到通知(因为WeakReference实例被回收前,它指向的Byte[]早已被回收,且实例自身回收后无法再入队到rq),最终WeakHashMap的size为40,代表未被回收的键的数量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 18:59:50