现在是2010/10/03 日凌晨4:50,可能是"WeakReference 与 PhantomReference 加入ReferenceQueue时机的疑惑 "一文中提出的问题没有解决一直睡不着吧:P 突然想到去check一下这个项目用的JRE,发现自己太粗心了,虽然Eclipse启动是用的SUN JRE1.6,但这个项目配置的却是系统上装的另一个IBM JRE1.5。所以在"WeakReference 与 PhantomReference 加入ReferenceQueue时机的疑惑 "一文的评论中我所贴的实际上是IBM JRE1.5的原码,所以我便开始怀疑是不是IBM的实现有问题? 于是将项目的JRE改过来后重新执行程序,结果就成了:
Phantom Reference refers to null in finalize(). Weak Reference refers to null in finalize(). java.lang.ref.WeakReference is added in the queue out of finalize().
首先,WeakReference终于是被放入到Queue中去了,但看样子像是在finalize()之后,其次SoftReference引用的Object索性就不回收了(这应该是符合定义的,softly reachable的对象应该还会再逗留一段时间)。但PhantomReference 还是没有被加入到Queue中去。于是去翻阅了一下Java Specification 3.0,没找到相关的内容,所以就去查了一下JDK1.6的Java Doc。首先它对Reachability的定义如下:
Going from strongest to weakest, the different levels of reachability reflect the life cycle of an object. They are operationally defined as follows:
-- An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it.
-- An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.
-- An object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference. When the weak references to a weakly-reachable object are cleared, the object becomes eligible for finalization.
-- An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.
-- Finally, an object is unreachable, and therefore eligible for reclamation, when it is not reachable in any of the above ways.
从中可见:WeakReference是在finalize()之前被clear的,这点IBM和SUN实现都一样。但还是没提加入Queue的时机。PhantomReference在finalize()之后还是会指向对象(依然没有加入到Queue中),这点也在两个JRE中印证了。
于是我们来看Notification 这段的描述:
这里就明确了,WeakReference应该在Object weakly reachable的时候被加入到Queue中,也就是说应该在finalize()之前。可见IBM JRE执行程序时不退出循环肯定是实现有问题的。SUN JRE虽然正常输出了,但为什么结果却显示不正确呢。于是我想到可能是System.gc()的问题,它并不保证finalize(),而我又在后面的循环中从Queue中取出了Reference,这有可能发生在finalize()之前。所以我将程序改进了一下:
import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; class TestObj { // 定义三种Reference用于保存指向本对象的Reference,从而在finalize中检验他们的get()值 private SoftReference sr = null; private WeakReference wr = null; private PhantomReference pr = null; // 定义ReferenceQueue用于在finalize中检验指向本对象的reference是否被加入了这个Queue private ReferenceQueue rq = null; // 有多少个对象被finalize()了 static private int finalizeCount = 0; synchronized public static int getFinalizeCount() { return finalizeCount; } synchronized public static void increaseFinalizeCount() { finalizeCount++; } public void setQueue(ReferenceQueue referenceQueue) { rq = referenceQueue; } public void setSr(SoftReference sr) { this.sr = sr; } public void setWr(WeakReference wr) { this.wr = wr; } public void setPr(PhantomReference pr) { this.pr = pr; } public void finalize() { // 检验指向本对象的Reference的get()值 if (null != sr) System.out.println("Soft Reference refers to " + sr.get() + " in finalize()."); if (null != wr) System.out.println("Weak Reference refers to " + wr.get() + " in finalize()."); if (null != pr) System.out.println("Phantom Reference refers to " + pr.get() + " in finalize()."); // 验证是不是在对象finalize()的时候他的soft reference已经被加入至Queue中 Reference r = rq.poll(); while (null != r) { System.out.println(r.getClass().getName() + " is added in the queue in finalize()."); r = rq.poll(); } increaseFinalizeCount(); } } public class TestReference { public static void main(String[] args) { { // 用于传给三种Reference的强引用 TestObj softObj = new TestObj(); TestObj weakObj = new TestObj(); TestObj phantomObj = new TestObj(); // 用于放三种Reference的三个Queue ReferenceQueue softrq = new ReferenceQueue(); ReferenceQueue weakrq = new ReferenceQueue(); ReferenceQueue phantomrq = new ReferenceQueue(); // 三种Reference指向三个不同的对象 SoftReference sr = new SoftReference(softObj, softrq); WeakReference wr = new WeakReference(weakObj, weakrq); PhantomReference pr = new PhantomReference(phantomObj, phantomrq); // 将Reference传回给对应的对象 softObj.setSr(sr); weakObj.setWr(wr); phantomObj.setPr(pr); // 将ReferenceQueue传回给对象 softObj.setQueue(softrq); weakObj.setQueue(weakrq); phantomObj.setQueue(phantomrq); // 删除强引用 softObj = null; weakObj = null; phantomObj = null; // 确保三个对象都被finalize() while (3 != TestObj.getFinalizeCount()) { System.gc(); } Reference r = phantomrq.poll(); if (null != r) System.out.println(r.getClass().getName() + " is added in the queue out of finalize()."); } } }
于是就看到了如下的结果:
Phantom Reference refers to null in finalize(). Weak Reference refers to null in finalize(). java.lang.ref.WeakReference is added in the queue in finalize(). Soft Reference refers to null in finalize(). java.lang.ref.SoftReference is added in the queue in finalize(). java.lang.ref.PhantomReference is added out of the queue in finalize().
前三行结果很快就出来了,后三行结果过了好一会儿才出来,程序正常结束。可见只要足够多次System.gc(),SoftReference引用的对象还是会被finalize()的,而PhantomReference确实是在object被回收的时候被加入了Queue。从上面的结果我们可以看出SoftReference和WeakReference确实是在finalize()之前就被clear并被加入到Queue中了。
于是又改回到IBM JRE1.5重新执行一下这个程序,结果就变成了:
Phantom Reference refers to null in finalize(). Weak Reference refers to null in finalize(). Soft Reference refers to null in finalize().
可见IBM的实现绝对是有问题的。ReferenceQueue根本没起到作用。
但为了让程序逻辑更清楚,有更清晰的输出,我又对上面这个程序进行了重写如下:
import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; //这个类负责check Reference什么时候被clear,以及什么时候被加入到Queue中 class CheckReference implements Runnable { // 要被check 的Reference private Reference r = null; // 用于check 上面的reference何时加入到Queue中的那个ReferenceQueue private ReferenceQueue rq = null; CheckReference(Reference r, ReferenceQueue rq) { this.r = r; this.rq = rq; } public void run() { //Check Reference何时被clear,PhantomReference的get()始终返回null,它被clear与加入Queue等价 if (!(r instanceof PhantomReference)) { while (r.get() != null){ // 调用GC,确保对象被finalize() System.gc(); } System.out.println(r.getClass().getName() + " is cleared."); } // check Reference何时被加入到了Queue中 Reference r = rq.poll(); while (null == r) { // 调用GC,确保对象被reclaim System.gc(); r = rq.poll(); } System.out.println(r.getClass().getName() + " is added in the queue."); } } class TestObj { private String name = "Anonymous"; TestObj(String name) { this.name = name; } public void finalize() { System.out.println("TestObj " + this.name + " is finalized."); } } public class TestReference { public static void main(String[] args) { { // 用于传给三种Reference的强引用 TestObj softObj = new TestObj("Soft"); TestObj weakObj = new TestObj("Weak"); TestObj phantomObj = new TestObj("Phantom"); // 用于放三种Reference的三个Queue ReferenceQueue softrq = new ReferenceQueue(); ReferenceQueue weakrq = new ReferenceQueue(); ReferenceQueue phantomrq = new ReferenceQueue(); // 三种Reference指向三个不同的对象 SoftReference sr = new SoftReference(softObj, softrq); WeakReference wr = new WeakReference(weakObj, weakrq); PhantomReference pr = new PhantomReference(phantomObj, phantomrq); // 创建对应三种Reference的check task CheckReference softCheck = new CheckReference(sr, softrq); CheckReference weakCheck = new CheckReference(wr, weakrq); CheckReference phantomCheck = new CheckReference(pr, phantomrq); // 创建对应的三个thread Thread softThread = new Thread(softCheck); Thread weakThread = new Thread(weakCheck); Thread phantomThread = new Thread(phantomCheck); // 把他们的优先级设高点增加他们被调用的机会,从而当一有变化时他们就能输出 softThread.setPriority(Thread.MAX_PRIORITY); weakThread.setPriority(Thread.MAX_PRIORITY); phantomThread.setPriority(Thread.MAX_PRIORITY); // 启动这三个线程 softThread.start(); weakThread.start(); phantomThread.start(); // 删除强引用 softObj = null; weakObj = null; phantomObj = null; } } }
发现这样做结果就变成了:
TestObj Weak is finalized. TestObj Phantom is finalized. java.lang.ref.WeakReference is cleared. java.lang.ref.WeakReference is added in the queue. java.lang.ref.PhantomReference is added in the queue.
并且程序始终结束不了。和之前的程序比较了半天,又想了半天,觉得应该是我一直调用SoftReference()的get()方法的问题,于是又将程序改写如下:
import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.concurrent.TimeUnit; //这个类负责check Reference什么时候被clear,以及什么时候被加入到Queue中 class CheckReference implements Runnable { // 要被check 的Reference private Reference r = null; // 用于check 上面的reference何时加入到Queue中的那个ReferenceQueue private ReferenceQueue rq = null; CheckReference(Reference r, ReferenceQueue rq) { this.r = r; this.rq = rq; } public void run() { // Check // Reference何时被clear,PhantomReference的get()始终返回null,它被clear与加入Queue等价 if (!(r instanceof PhantomReference)) { if (r instanceof WeakReference) { while (r.get() != null) { // 调用GC,确保对象被finalize() System.gc(); } } else { // 不调用SoftReference的get() while (3 != TestObj.getFinalizeCount()) // 调用GC,确保对象被finalize() System.gc(); } System.out.println(r.getClass().getName() + " is cleared."); } // check Reference何时被加入到了Queue中 Reference r = rq.poll(); while (null == r) { // 调用GC,确保对象被reclaim System.gc(); r = rq.poll(); } System.out.println(r.getClass().getName() + " is added in the queue."); // 睡一会儿,以防程序马上终止导致finalize()还没执行完。 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } enum ReferenceLevel { STRONG, SOFT, WEAK, PHANTOM } class TestObj { private String name = "Anonymous"; // 有多少个对象被finalize()了 static private int finalizeCount = 0; // 定义指向本对象的引用级别 private ReferenceLevel rf = ReferenceLevel.STRONG; public void setRf(ReferenceLevel rf) { this.rf = rf; } synchronized public static int getFinalizeCount() { return finalizeCount; } synchronized public static void increaseFinalizeCount() { finalizeCount++; } TestObj(String name) { this.name = name; } public void finalize() { increaseFinalizeCount(); // 如果是SoftReference, 睡一会儿,让Check Task有机会被调度 if (ReferenceLevel.SOFT.equals(this.rf)) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("TestObj " + this.name + " is finalized."); } } public class TestReference { public static void main(String[] args) { { // 用于传给三种Reference的强引用 TestObj softObj = new TestObj("Soft"); TestObj weakObj = new TestObj("Weak"); TestObj phantomObj = new TestObj("Phantom"); // 用于放三种Reference的三个Queue ReferenceQueue softrq = new ReferenceQueue(); ReferenceQueue weakrq = new ReferenceQueue(); ReferenceQueue phantomrq = new ReferenceQueue(); // 三种Reference指向三个不同的对象 SoftReference sr = new SoftReference(softObj, softrq); WeakReference wr = new WeakReference(weakObj, weakrq); PhantomReference pr = new PhantomReference(phantomObj, phantomrq); // 设置引用级别 softObj.setRf(ReferenceLevel.SOFT); weakObj.setRf(ReferenceLevel.WEAK); phantomObj.setRf(ReferenceLevel.PHANTOM); // 创建对应三种Reference的check task CheckReference softCheck = new CheckReference(sr, softrq); CheckReference weakCheck = new CheckReference(wr, weakrq); CheckReference phantomCheck = new CheckReference(pr, phantomrq); // 创建对应的三个thread Thread softThread = new Thread(softCheck); Thread weakThread = new Thread(weakCheck); Thread phantomThread = new Thread(phantomCheck); // 把他们的优先级设高点增加他们被调用的机会,从而当一有变化时他们就能输出 softThread.setPriority(Thread.MAX_PRIORITY); weakThread.setPriority(Thread.MAX_PRIORITY); phantomThread.setPriority(Thread.MAX_PRIORITY); // 启动这三个线程 softThread.start(); weakThread.start(); phantomThread.start(); // 删除强引用 softObj = null; weakObj = null; phantomObj = null; } } }
结果就变成了:
java.lang.ref.WeakReference is cleared. java.lang.ref.WeakReference is added in the queue. TestObj Phantom is finalized. TestObj Weak is finalized. java.lang.ref.PhantomReference is added in the queue. java.lang.ref.SoftReference is cleared. java.lang.ref.SoftReference is added in the queue. TestObj Soft is finalized.
终于达到想要的结果了,执行了几次结果都是相同的。
总结:
1) SoftReference在当object softly reachable 的时候并不一定会被clear 和加入Reference Queue中,这主要由当前内存使用是否吃紧与SoftReference是否还在经常被使用相关(get()方法是否还在经常被调用)。但clear和被加入Reference Queue中的时机都在finalize()之前。
2) WeakReference在当object weakly reachable的时候就会被clear 和加入Reference Queue中,并且都在finalize()之前。
3) PhantomReference只有在Object真正被回收的时候才被加入到Reference Queue中,是在 finalize()被调用之后的下次GC中。
4) 三种Reference都是在GC的时候才被决定是否clear 和加入Queue中的。