【转】Java中WeakReference、WeakHashMap、SoftReference、ReferenceQueue的作用和使用场景
http://blog.csdn.net/yhmhappy2006/article/details/6731108
详见以下测试代码及其注释:
package test; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; 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.Map; import java.util.WeakHashMap; import org.junit.Test; class VeryBig { private static final int SIZE = 1000; private double[] la = new double[SIZE]; private String ident; public VeryBig(String id) { ident = id; } public VeryBig(String id, int s) { ident = id; la = new double[s]; } public String toString() { return ident; } protected void finalize() { System.out.println("Finalizing " + ident); } } public class ReferencesTest { /** * Strong Reference<br> * 是 Java 的默认引用实现, 它会尽可能长时间的存活于 JVM 内, 当没有任何强引用指向对象时, GC 执行后将会回收对象 */ @Test public void strongReference() { System.out.println("start test strongReference"); Object referent = new VeryBig("strongReference"); /** * 通过赋值创建 StrongReference */ Object strongReference = referent; assertSame(referent, strongReference); referent = null; System.gc(); /** * 由于对象还存在引用,因此gc后,对象未被回收 */ assertNotNull(strongReference); System.out.println("end test strongReference"); } /** * WeakReference<br> * 顾名思义, 是一个弱引用, 当所引用的对象在 JVM 内不再有强引用时, GC后,弱引用被置成null,并回收所指的对象 */ @Test public void weakReference() { System.out.println("start test weakReference"); Object referent = new VeryBig("weakReference"); WeakReference<Object> weakRerference = new WeakReference<Object>( referent); assertSame(referent, weakRerference.get()); referent = null; System.gc(); /** * 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被置成null,对象就可以被 回收了。。。 */ assertNull(weakRerference.get()); System.out.println("end test weakReference"); } /** * WeakHashMap<br> * 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 * entry * * @throws InterruptedException */ @Test public void weakHashMap() throws InterruptedException { Map<Object, Object> weakHashMap = new WeakHashMap<Object, Object>(); Object key = new VeryBig("weakHashMap key"); Object value = new Object(); weakHashMap.put(key, value); assertTrue(weakHashMap.containsValue(value)); key = null; System.gc(); /** * 等待无效 entries 进入 ReferenceQueue 以便下一次调用 getTable 时被清理 */ Thread.sleep(1000); /** * 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry */ assertFalse(weakHashMap.containsValue(value)); } /** * SoftReference<br> * * 与 WeakReference 的特性基本一致, 最大的区别在于 SoftReference会尽可能长的保留引用,不会在GC时就回收对象,而是直到 * JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用 */ @Test public void softReference() { Object referent = new VeryBig("softReference"); SoftReference<Object> softRerference = new SoftReference<Object>( referent); assertNotNull(softRerference.get()); referent = null; System.gc(); /** * soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用 */ assertNotNull(softRerference.get()); // make oom.... try { new VeryBig("oom ", 100000000); } catch (Throwable e) { } assertNull(softRerference.get()); } /**原文中的这个例子没讲明白,见我后面添加的例子 * PhantomReference<br> * * Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同, 因为它的 * get() 方法永远返回 null, 这也正是它名字的由来 */ @Test public void phantomReferenceAlwaysNull() { Object referent = new VeryBig("phantomReferenceAlwaysNull"); ReferenceQueue<Object> q = new ReferenceQueue<Object>(); PhantomReference<Object> phantomReference = new PhantomReference<Object>( referent, q); /** * phantom reference 的 get 方法永远返回 null */ assertNull(phantomReference.get()); assertNull(q.poll()); System.gc(); assertNull(q.poll()); } /** * RererenceQueue<br> * * 当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作. 将一个 * ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个weak ref插入到 * ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 * entries * * @throws InterruptedException */ @Test public void referenceQueueWithWeakReference() throws InterruptedException { Object referent = new VeryBig("referenceQueueWithWeakReference"); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); Reference<Object> ref = new WeakReference<Object>(referent, referenceQueue); assertFalse(ref.isEnqueued()); Reference<? extends Object> polled = referenceQueue.poll(); assertNull(polled); referent = null; System.gc(); assertTrue(ref.isEnqueued()); Reference<? extends Object> removed = referenceQueue.remove(); assertNotNull(removed); assertSame(ref, removed); assertNull(removed.get()); } @Test public void referenceQueueWithSoftReference() throws InterruptedException { Object referent = new VeryBig("referenceQueueWithWeakReference"); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); Reference<Object> ref = new SoftReference<Object>(referent, referenceQueue); assertFalse(ref.isEnqueued()); Reference<? extends Object> polled = referenceQueue.poll(); assertNull(polled); referent = null; // make oom.... try { new VeryBig("oom ", 100000000); } catch (Throwable e) { } assertTrue(ref.isEnqueued()); Reference<? extends Object> removed = referenceQueue.remove(); assertNotNull(removed); assertSame(ref, removed); assertNull(removed.get()); } }
参考:
http://www.iteye.com/topic/401478
http://zhang-xzhi-xjtu.iteye.com/blog/484934
http://zhang-xzhi-xjtu.iteye.com/blog/413159
--------------
参考:
http://blog.csdn.net/huoyin/article/details/5891998
SoftReference
类的一个典型用途就是用于内存敏感的高速缓存。 SoftReference
的原理是:在保持对对象的引用时保证在 JVM 报告内存不足情况之前将清除所有的软引用。关键之处在于,垃圾收集器在运行时可能会(也可能不会)释放软可及对象。对象是否被释放取决于垃圾收集器的算法以及垃圾收集器运行时可用的内存数量。
WeakReference
类的一个典型用途就是规范化映射(canonicalized mapping)。另外,对于那些生存期相对较长而且重新创建的开销也不高的对象来说,弱引用也比较有用。关键之处在于,垃圾收集器运行时如果碰到了弱可及对象,将释放 WeakReference
引用的对象。然而,请注意,垃圾收集器可能要运行多次才能找到并释放弱可及对象。
PhantomReference
类只能用于跟踪对被引用对象即将进行的收集。同样,它还能用于执行 pre-mortem 清除操作。 PhantomReference
必须与 ReferenceQueue
类一起使用。需要 ReferenceQueue
是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时, PhantomReference
对象就被放在它的 ReferenceQueue
上。将 PhantomReference
对象放在 ReferenceQueue
上也就是一个通知,表明 PhantomReference
对象引用的对象已经结束,可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。
对于phantom类见此例:
public static void main(String[] args) throws InterruptedException { Object test = new Object(); ReferenceQueue<Object> q = new ReferenceQueue<Object>(); PhantomReference<Object> p = new PhantomReference<Object>(test, q); Object lock = new Object(); while (true) { System.out.println("Checking queue..."); if (q.poll() != null) { break; } System.out.println("Still polling..."); System.gc(); try { Thread.sleep(500); } catch (InterruptedException e) { System.out.println("Interrupted!"); break; } // Make the object garbage... test = null; } System.out.println("Finished."); }
输出结果:
Checking queue... Checking queue... Still polling... Checking queue... Finished.
Still polling...
由上例可看出,GC后需要等待一段时间才能由ReferenceQueue中得知phantom引用已被GC了。