虚引用必须与ReferenceQueue配合使用,因为其get()方法恒返回null,无法访问对象,JVM仅在回收前将虚引用入队到关联队列,唯有轮询或阻塞获取队列中引用才能获知回收事件。

虚引用(PhantomReference)是 Java 四种引用类型中最弱的一种,它**无法通过它获取到对象实例**,唯一作用就是在对象被垃圾回收时收到系统通知。而这个“通知”机制,必须配合 ReferenceQueue 才能实现。
为什么虚引用必须和 ReferenceQueue 一起用?
虚引用的 get() 方法永远返回 null,所以它不持有对象的访问路径。JVM 在准备回收一个只被虚引用持有的对象前,会把该虚引用**入队(enqueue)** 到关联的 ReferenceQueue 中。只有从队列里取出虚引用,才能知道“这个对象刚被回收了”。没有队列,你就完全收不到回收信号。
典型配合写法:构造 + 入队监听
创建虚引用时必须传入一个非 null 的 ReferenceQueue;之后通过轮询或阻塞方式从队列中取引用,从而触发清理逻辑:
- 创建时绑定:
PhantomReference<MyObj> phRef = new PhantomReference<>(obj, refQueue); - 启动一个线程(或在合适时机)调用
refQueue.remove()或refQueue.poll() -
remove()会阻塞直到有引用入队;poll()立即返回(可能为 null) - 取到的虚引用可用来识别是哪个对象被回收(例如通过自定义子类或关联标识)
实际使用中的关键细节
虚引用不会阻止对象回收,但它的存在本身需要被 GC 处理 —— 虚引用对象自身也会被回收。因此常见实践包括:
立即学习“Java免费学习笔记(深入)”;
- 不要在虚引用对象里保存强引用,否则可能造成内存泄漏
- 推荐用继承
PhantomReference的子类,在构造时存下可识别信息(如 ID、资源句柄) - 清理逻辑(如释放堆外内存、关闭文件描述符)必须放在从队列取出后执行,不能依赖对象本身(它已不可达)
- 注意:对象进入
ReferenceQueue时,其finalize()(如果重写过)**已经执行完毕**(如果有的话),且对象处于“finalized but not yet reclaimed”状态
一个极简示例结构
不是完整可运行代码,而是突出协作关系:
ReferenceQueue<MyResource> queue = new ReferenceQueue<>();
MyResource obj = new MyResource();
PhantomReference<MyResource> phRef = new PhantomReference<>(obj, queue);
<p>// 后续某处监听:
new Thread(() -> {
while (true) {
try {
PhantomReference<MyResource> ref = (PhantomReference<MyResource>) queue.remove();
// 此时 obj 已被回收,可安全清理关联资源
cleanupNativeResources(ref);
} catch (InterruptedException e) { break; }
}
}).start();</p>


















