Strong Reference, 强引用,即java标准的引用方式,表示GC从 Root Set 开始向下扫描,可以找到对应的 Strong Reference。
Referent,被包装为 Weak, Soft, Phantom Reference的对象引用称之为 referent。后面的内容会多次提到这个名词。
Weak Reference, 弱引用。当一个referent,在运行时没有同时被强,软引用,只被Weak Reference自身引用,且Weak Reference从 Root Set 可达,则该referent会被GC回收。
WR的作用,一般是为referent提供一个被回收的凭据,结合ReferenceQueue可以让程序在第一时间得到referent被回收的事件,从而做一些额外的clean操作。(如果对ReferenceQueue作用和回调感兴趣,可以先看最下面的 ReferenceQueue 简介)
Soft Reference, 软引用。它是除strong外,生命周期最长的一种 Reference,只有当JVM Heap中充满Strong References, Full GC无法为heap腾出更多空间而即将抛出OOM时,SoftReferences会被GC回收。
SR的作用一般是用作不限大小的 cache(无需remove)。
比如将其 Soft Reference 无限地放入 hashmap 中,而不关心hashmap内的对象数量是否会撑爆heap,也不需要手动 remove。
当Heap容量达到OOM触发条件时,VM会自动回收该map里的所有SoftReferences.
Phanton Reference, 是一种特殊的Reference,正如他的名字所表达的,幻影引用,他可以像幻影一样附着在referent上。
当GC在遍历引用关系时,如果发现被phantom reference包装过的referent不存在strong, weak, soft引用时(就是除phantom外没有任何引用,幻影的由来),GC会将 phantom reference 放入 Reference queue。以便程序在另一边通过queue的remove/poll方法,感知referent被GC回收的事件。(如果对ReferenceQueue作用和回调感兴趣,可以先看最下面 ReferenceQueue 简介)
另外,我们知道,GC在回收对象前会先调用对象自身的finalize()方法,如果它有实现的话,然后再清掉内存。而Phantom Reference的回调(enqueue)是在对象的finalize后,回收前触发。这跟 WeakReference不一样。WR是在回收后才通知的。在这个特殊的阶段可以做一些特殊的clean操作。
为什么 Phantom Reference 的get总是返回null?
因为phantom reference想做到幻影(除自身外,不跟其他任何引用有关联),所以不允许程序能通过自身的get方法得到referent,而破坏幻影的初衷。
实例代码
package com.kenwublog.reference; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.util.HashMap; public class PhantomReferenceTest { public static void main(String[] args) { ReferenceQueue referenceQueue = new ReferenceQueue(); Object object = new Object() { public String toString() { return "Referenced Object"; } }; Object data = new Object() { public String toString() { return "Data"; } }; HashMap map = new HashMap(); Reference reference = null; System.out.println("Testing PhantomReference."); reference = new PhantomReference(object, referenceQueue); map.put(reference, data); System.out.println(reference.get()); // null System.out.println(map.get(reference)); // Data System.out.println(reference.isEnqueued()); // false System.gc(); System.out.println(reference.get()); // null System.out.println(map.get(reference)); // Data System.out.println(reference.isEnqueued()); // false object = null; data = null; System.gc(); System.out.println(reference.get()); // null System.out.println(map.get(reference)); // Data System.out.println(reference.isEnqueued()); // true, because object has been reclaimed. } }
ReferenceQueue,一种当 weak, soft, phantom的referent被GC回收后,提供事件回调的接口。需要在实例化三大reference时,通过构造函数传入,phantom reference是强制需要传入的,weak和soft可不传。
回调过程:
GC回收referent后(phantom是在回收前,finalize后),将reference enqueue到RQ中,程序通过调用RQ的remove方法来感知reference被GC回收的事件。
remove方法是阻塞的,当没有referent被回收时(GC未调用enqueue),remove方法会一直挂起线程,当有referent被回收时,该方法返回 referent对应的reference对象。
同样,RQ也提供了一个非阻塞的方法 poll,但这样就做不到实时回调了。
实例
package com.kenwublog.reference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.HashMap; public class ReferenceQueueTest { public static void main(String[] args) { final ReferenceQueue q = new ReferenceQueue(); String str = new String("AK47"); WeakReference wr = new WeakReference(str, q); Thread t = new Thread(){ @Override public void run() { try { Reference reference = q.remove(); System.out.println(reference + " event fired."); } catch (InterruptedException e) { e.printStackTrace(); } } }; t.setDaemon(true); t.start(); System.out.println("Reference Queue is listening."); str = null; // clear strong reference System.out.println("Ready to gc"); System.gc(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("wr.get: " + wr.get()); } }
参考资料
http://mindprod.com/jgloss/phantom.html (References的目的,在GC的哪种阶段触发对比表格)
http://www.pawlan.com/monica/articles/refobjs/