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
相关产品推荐
相关产品推荐

