Java 2 平台引入了 java.lang.ref 包,其中包括的类可以让您引用对象,而不将它们留在内存中。这些类还提供了与垃圾收集器(garbage collector)之间有限的交互。
垃圾收集器和引用交互
垃圾收集器每次运行时都可以随意地释放不再是强可及的对象占用的内存。如果垃圾收集器发现了软可及对象,就会出现下列情况:
SoftReference 对象的 referent 域被设置为 null,从而使该对象不再引用 heap 对象。
SoftReference 引用过的 heap 对象被声明为 finalizable。
当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放,SoftReference 对象就被添加到它的 ReferenceQueue(如果后者存在的话)。
如果垃圾收集器发现了弱可及对象,就会出现下列情况:
WeakReference 对象的 referent 域被设置为 null,从而使该对象不再引用 heap 对象。
WeakReference 引用过的 heap 对象被声明为 finalizable。
当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放时,WeakReference 对象就被添加到它的 ReferenceQueue(如果后者存在的话)。
如果垃圾收集器发现了虚可及对象,就会出现下列情况:
PhantomReference 引用过的 heap 对象被声明为 finalizable。
与软引用和弱引用有所不同,PhantomReference 在堆对象被释放之前就被添加到它的 ReferenceQueue。(请记住,所有的 PhantomReference 对象都必须用经过关联的 ReferenceQueue 来创建。)这使您能够在堆对象被回收之前采取行动。
请考虑清单 1 中的代码。图 1 说明了这段代码的执行情况。
清单 1. 使用 WeakReference 及 ReferenceQueue 的示例代码
//Create a strong reference to an objectMyObject obj = new MyObject();
//1//Create a reference queueReferenceQueue rq = new ReferenceQueue();
//2 //Create a weakReference to obj and associate our reference queueWeakReference wr = new WeakReference(obj, rq); //3
图 1. 执行了清单 1 中行 //1、//2 和 //3 的代码之后的对象布局
图 1 显示了每行代码执行后各对象的状态。行 //1 创建 MyObject 对象,而行 //2 则创建 ReferenceQueue 对象。行 //3 创建引用其引用对象 MyObject 的 WeakReference 对象,还创建它的 ReferenceQueue。请注意,每个对象引用(obj、rq 及 wr)都是强引用。要利用这些引用类,您必须取消对 MyObject 对象的强引用,方法是将 obj 设置为 null。前面说过,如果不这样做,对象 MyObject 永远都不会被回收,引用类的任何优点都会被削弱。
每个引用类都有一个 get() 方法,而 ReferenceQueue 类有一个 poll() 方法。get() 方法返回对被引用对象的引用。在 PhantomReference 上调用 get() 总是会返回 null。这是因为 PhantomReference 只用于跟踪收集。poll() 方法返回已被添加到队列中的引用对象,如果队列中没有任何对象,它就返回 null。因此,执行清单 1 之后再调用 get() 和 poll() 的结果可能是:
wr.get();
//returns reference to MyObjectrq.poll();
//returns null