WeakReference 必须显式绑定 ReferenceQueue 才能感知对象回收,轮询队列需主动调用 poll() 或 remove(),入队的是引用本身而非原对象,清理逻辑需手动实现且上下文信息须提前保存。

WeakReference 本身不执行清理,必须搭配 ReferenceQueue 才能感知对象已被回收;关键不是“等 GC 发生”,而是主动从队列中取出已失效的引用,并在此时执行资源释放逻辑。
绑定队列是前提,不是可选项
WeakReference 必须在构造时显式传入 ReferenceQueue 实例,否则 JVM 不会将它加入任何队列——哪怕对象已被回收,你也收不到通知。
-
错误写法:
new WeakReference(obj)—— 队列未关联,queue.poll()永远返回null -
正确写法:
new WeakReference(obj, queue)—— 绑定成功,GC 后该引用实例会被入队 - 同一个
ReferenceQueue可被多个WeakReference(甚至PhantomReference)共用,但不能跨 JVM
轮询队列要主动,不能靠“自动回调”
Java 没有对象回收时自动触发方法的机制。ReferenceQueue 是一个信号通道,你得自己去查有没有新成员进来。
-
queue.poll()立即返回,适合嵌入业务逻辑末尾(如缓存get()或put()后检查),避免阻塞主线程 -
queue.remove(100)带超时等待,适合独立守护线程,比无条件remove()更安全 - 不要刚创建引用就立刻轮询——GC 尚未发生,队列必然为空
清理时拿不到原对象,上下文得提前存好
入队的是 WeakReference 实例本身,不是它曾经指向的对象。原始对象在入队前已被 GC 回收,ref.get() 此时一定为 null。
- 若需关闭文件句柄、释放数据库连接,必须在创建引用前把关键信息(如路径、ID、超时时间)保存下来
- 推荐方式:自定义子类继承
WeakReference,把元数据作为字段存储 - 避免反模式:
map.put(ref, resource)—— ref 入队后 resource 仍留在 map 中,造成泄漏
清理动作必须手动触发,队列只是通知器
ReferenceQueue 不会帮你关流、删缓存、释放内存。它只负责告诉你:“这个引用已经失效了”。后续所有操作都得由你编码完成。
- 典型流程:
ref = queue.poll()→ 判断非空 → 调用cleanup(ref)→ 从本地缓存 Map 中移除对应 key → 关闭关联资源 - 别依赖
finalize():已被标记为 deprecated,执行时机不确定,且严重拖慢 GC - 慎用
PhantomReference:虽更安全,但get()恒为null,必须额外维护映射表,复杂度更高










