WeakReference + ReferenceQueue 仅被动通知对象已被GC回收,需同时满足 ref.get() == null 且 queue.poll() != null 才可靠判定;入队的是引用对象而非原数据,须提前保存标识,且不可依赖 System.gc()。

WeakReference + ReferenceQueue 能感知对象被 GC 的时刻,但不能保证“立刻”或“可靠”——它只在 GC 完成且弱引用被清除后入队,不是监听器,而是回收事件的被动接收者。
WeakReference 必须关联 ReferenceQueue 才能触发入队
单独创建 WeakReference 不会自动入队;只有构造时传入 ReferenceQueue,GC 清除该引用后,JVM 才会把 WeakReference 实例本身放入队列。
-
WeakReference<bigdata> ref = new WeakReference(data, queue);</bigdata>—— 正确,queue是必需参数 -
WeakReference<bigdata> ref = new WeakReference(data);</bigdata>—— 错误,后续调用queue.poll()永远为空,因为没绑定 - 注意:入队的是
WeakReference对象,不是它曾指向的data;所以你要提前保存关键标识(比如 ID、size)到引用对象上,或用WeakReference子类携带元数据
poll() 返回 null 不代表对象还活着
queue.poll() 返回 null 只说明“当前没有待处理的已回收引用”,不代表目标对象仍存活。GC 是否发生、是否清理该弱引用,完全不可控。
- 常见错误:在
data = null;后立刻queue.poll(),结果为null,就断定“还没回收”——其实只是 GC 还没跑,或这次 GC 没清它 - 测试时可加
System.gc(); Thread.sleep(10);循环几次,但生产环境严禁依赖System.gc(),它只是建议,JVM 可忽略 - 真正可靠的判断逻辑是:
ref.get() == null && queue.poll() != null,二者需同时成立才表示“已被回收并通知”
大内存对象监控必须避免“复活”和误判
WeakReference 本身不阻止回收,但若你在 ref.get() 返回非空后又长期持有该返回值,等于人为延长生命周期,导致监控失效。
- 别缓存
ref.get()的结果;每次需要访问对象状态时,都应重新调用get()并检查是否为null - 一旦
queue.poll()返回非空引用,立即调用ref.clear(),防止后续意外调用get()拿到已不可靠的对象 - 如果对象持有堆外资源(如 DirectByteBuffer、native handle),WeakReference 不够安全——必须换用
PhantomReference,否则可能在get()返回非空时访问到已释放资源 - 监控线程建议用守护线程(
setDaemon(true)),避免阻塞 JVM 退出;轮询间隔不宜过密(如 50ms),否则白耗 CPU
ReferenceQueue 的本质是 GC 回收流程末端的一个轻量信号通道,它不提供实时性、不承诺顺序、也不做重试。真正难的不是写对这几行代码,而是接受它的“尽力而为”——你得在业务逻辑里预留 GC 延迟、并发竞争、以及对象实际存活时间远超预期的可能性。










