也许以下程序会有所帮助:
public class SimpleGCExample { public static void main(String[] args) throws InterruptedException { ReferenceQueue<Object> queue=new ReferenceQueue<>(); SimpleGCExample e = new SimpleGCExample(); Reference<Object> pRef=new PhantomReference<>(e, queue), wRef=new WeakReference<>(e, queue); e = null; for(int count=0, collected=0; collected<2; ) { Reference ref=queue.remove(100); if(ref==null) { System.gc(); count++; } else { collected++; System.out.println((ref==wRef? "weak": "phantom") +" reference enqueued after "+count+" gc polls"); } } } @Override protected void finalize() throws Throwable { System.out.println("finalizing the object in "+Thread.currentThread()); Thread.sleep(100); System.out.println("done finalizing."); }}在我的系统上,它会打印
weak reference enqueued after 1 gc pollsfinalizing the object in Thread[Finalizer,8,system]done finalizing.phantom reference enqueued after 2 gc polls
要么
finalizing the object in Thread[Finalizer,8,system]weak reference enqueued after 1 gc pollsdone finalizing.phantom reference enqueued after 2 gc polls
由于多线程,前两个消息的顺序有时会有所不同。有时,幻象引用被报告在三个轮询之后加入队列,这表明它花费了超过指定的100毫秒的时间。
关键是
- 开始完成时,软引用和弱引用被清除并排入队列
- 幻象引用 在 完成 后 排队,假设该对象没有泄漏该
finalize
方法,否则在对象再次变得不可访问之后将它们排队 - (非平凡的)
finalize()
方法的存在导致需要至少一个额外的垃圾收集周期来检测对象不可达或幻像可达
由于所有对象中超过99%的对象不需要完成,因此强烈建议所有JVM供应商检测何时
finalize()未被覆盖或“琐碎”,即空方法或单独
super.finalize()调用。在这些情况下,应该省略最终确定步骤。通过删除
finalize()上面示例中的方法,您可以轻松检查此优化是否在JVM中发生。然后打印
weak reference enqueued after 1 gc pollsphantom reference enqueued after 1 gc polls
由于两者都立即入队并以任意顺序检索,因此这两个消息的顺序可能会有所不同。但是它们总是在一个gc周期后都被排入队列。
值得注意的是,幻像引用不会自动清除,这意味着它需要另一个垃圾回收周期,直到对象的内存真正可以重用为止,因此上面的示例至少需要三个周期(使用非平凡
finalize()方法),两个不使用。Java
9将对此进行更改,自动清除幻像引用,因此在上面的示例中,将需要两个周期进行最终确定,而一个周期直到真正可以回收内存为止。好吧,准确地说,在这个简单的示例中,对象的内存将永远不会被回收,因为程序会在此之前终止。
上面的代码还演示了Reference
API的预期用例之一。我们可以使用它来检测对象的可访问性何时在我们的完全控制下的代码中更改,例如,使用
main方法内的循环。相反,
finalize()可以在任意时间由不同的未指定线程调用。该示例还显示,您可以从参考对象中提取信息,而无需使用该
get()方法。
实际应用程序经常使用引用类的子类向它们添加更多信息。这就是
WeakHashMap.Entry扩展
WeakReference和记住哈希码和值的过程。清理可以在常规映射操作中完成,不需要任何线程同步。
finalize()除了地图实现无法将
finalize()方法推入键类之外,使用方法是不可能的。
这意味着“比完成更灵活”一词。
该
WeakHashMap演示了
get()方法是有用的。只要尚未收集密钥,就会将其报告为在地图中,并且可以在遍历所有密钥或条目时进行检索。
该
PhantomReference.get()方法已被覆盖以始终返回,
null以防止应用程序可以重新占用已排队引用的引用。这是“虚拟引用不会自动清除”规则的直接结果。该规则本身令人怀疑,其初衷是在黑暗中。虽然该规则将在下一个Java版本中进行更改,但是恐怕
get()它将继续返回
null向后兼容。



