`
Feiing
  • 浏览: 237910 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

理解 Java 的 GC 与 幽灵引用

阅读更多

理解 Java 的 GC 与 幽灵引用
 
         Java 中一共有 4 种类型的引用 : StrongReference、 SoftReference、 WeakReference 以及 PhantomReference (传说中的幽灵引用 呵呵),
这 4 种类型的引用与 GC 有着密切的关系,  让我们逐一来看它们的定义和使用场景 :

        1. Strong Reference
       
        StrongReference 是 Java 的默认引用实现,  它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时 GC 执行后将会被回收

	@Test
	public void strongReference() {
		Object referent = new Object();
		
		/**
		 * 通过赋值创建 StrongReference 
		 */
		Object strongReference = referent;
		
		assertSame(referent, strongReference);
		
		referent = null;
		System.gc();
		
		/**
		 * StrongReference 在 GC 后不会被回收
		 */
		assertNotNull(strongReference);
	}
	





        2. WeakReference & WeakHashMap

WeakReference, 顾名思义,  是一个弱引用,  当所引用的对象在 JVM 内不再有强引用时, GC 后 weak reference 将会被自动回收

	@Test
	public void weakReference() {
		Object referent = new Object();
		WeakReference<Object> weakRerference = new WeakReference<Object>(referent);
	
		assertSame(referent, weakRerference.get());
		
		referent = null;
		System.gc();
		
		/**
		 * 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收
		 */
		assertNull(weakRerference.get());
	}
	




WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry

	@Test
	public void weakHashMap() throws InterruptedException {
		Map<Object, Object> weakHashMap = new WeakHashMap<Object, Object>();
		Object key = new Object();
		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));
	}
	




        3. SoftReference

SoftReference 于 WeakReference 的特性基本一致, 最大的区别在于 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用

	@Test
	public void softReference() {
		Object referent = new Object();
		SoftReference<Object> softRerference = new SoftReference<Object>(referent);
	
		assertNotNull(softRerference.get());
		
		referent = null;
		System.gc();
		
		/**
		 *  soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用
		 */
		assertNotNull(softRerference.get());
	}

	




        4. PhantomReference

        作为本文主角, Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同,  因为它的 get() 方法永远返回 null, 这也正是它名字的由来

	@Test
	public void phantomReferenceAlwaysNull() {
		Object referent = new Object();
		PhantomReference<Object> phantomReference = new PhantomReference<Object>(referent, new ReferenceQueue<Object>());
		
		/**
		 * phantom reference 的 get 方法永远返回 null 
		 */
		assertNull(phantomReference.get());
	}

	



         诸位可能要问, 一个永远返回 null 的 reference 要来何用,  请注意构造 PhantomReference 时的第二个参数 ReferenceQueue(事实上 WeakReference & SoftReference 也可以有这个参数),
PhantomReference 唯一的用处就是跟踪 referent  何时被 enqueue 到 ReferenceQueue 中.

     5. RererenceQueue

当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作.   将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.

	@Test
	public void referenceQueue() throws InterruptedException {
		Object referent = new Object();		
		ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
		WeakReference<Object> weakReference = new WeakReference<Object>(referent, referenceQueue);
		
		assertFalse(weakReference.isEnqueued());
		Reference<? extends Object> polled = referenceQueue.poll();
		assertNull(polled);
		
		referent = null;
		System.gc();

		assertTrue(weakReference.isEnqueued());
		Reference<? extends Object> removed = referenceQueue.remove();
		assertNotNull(removed);
	}



6.  PhantomReference  vs WeakReference

PhantomReference  有两个好处, 其一, 它可以让我们准确地知道对象何时被从内存中删除, 这个特性可以被用于一些特殊的需求中(例如 Distributed GC,  XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作).

其二, 它可以避免 finalization 带来的一些根本性问题, 上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中,  但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?
这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用,  这将导致这一轮的 GC 无法回收这个对象并有可能
引起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用,  也就不会出现上述问题,  当然这是一个很极端的例子, 一般不会出现.

7. 对比

taken from http://mindprod.com/jgloss/phantom.html

Soft vs Weak vs Phantom References Type Purpose Use When GCed Implementing Class
Strong Reference An ordinary reference. Keeps objects alive as long as they are referenced. normal reference. Any object not pointed to can be reclaimed. default
Soft Reference Keeps objects alive provided there’s enough memory. to keep objects alive even after clients have removed their references (memory-sensitive caches), in case clients start asking for them again by key. After a first gc pass, the JVM decides it still needs to reclaim more space. java.lang.ref.SoftReference
Weak Reference Keeps objects alive only while they’re in use (reachable) by clients. Containers that automatically delete objects no longer in use. After gc determines the object is only weakly reachable java.lang.ref.WeakReference 
java.util.WeakHashMap
Phantom Reference Lets you clean up after finalization but before the space is reclaimed (replaces or augments the use offinalize()) Special clean up processing After finalization. java.lang.ref.PhantomReference


8. 小结
       一般的应用程序不会涉及到 Reference 编程, 但是了解这些知识会对理解 GC 的工作原理以及性能调优有一定帮助,   在实现一些基础性设施比如缓存时也可能会用到, 希望本文能有所帮助.

        王政 于 2009,6,3

       

 

分享到:
评论
49 楼 cry615 2011-12-26  
帖子很不错,java里任何一个东西都是一门学问,很有很强的逻辑性和设计能力。
48 楼 Feiing 2010-05-19  
taowen 写道
如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。


把这个老帖子翻出来是因为今天一个同事看到 google-collections MapMaker 已经提供了类似功能

    ConcurrentMap<Key, Graph> graphs = new MapMaker()
        .concurrencyLevel(32)
        .softKeys()
        .weakValues()
        .expiration(30, TimeUnit.MINUTES)
        .makeComputingMap(
            new Function<Key, Graph>() {
              public Graph apply(Key key) {
                return createExpensiveGraph(key);
              }
            });}


key 和  value 的 reference 类型以及 expiration strategy 都可以指定, 用起来很方便
47 楼 NK_HansWang 2009-12-04  
看不懂啊 需要提前预备哪些知识吗?
46 楼 wjn811 2009-10-13  
jiyanliang 写道
patrickyao1988 写道
今天才知道原来还有这么多种引用类型的啊。。。请问有关这方面的什么书讲的比较好呢?

thinking in java中介绍 对象的集合 时在介绍持有引用时提到了一些,但是很可惜,不是很详细。。。

深入java虚拟机 第二版 第9章 也有讲到
45 楼 frenchmay 2009-09-18  
看了boost的 Smart_ptr, 感觉很神似
44 楼 java.lang.Object 2009-09-18  
<div class="quote_title">solonote 写道</div>
<div class="quote_div">
<p>ConcurrentHashMap&lt;String, SoftReference&gt;<br>刚才自己捣鼓了一下,确实没有什么好办法可以保证当SoftReference被回收时,Key也自动被Remove.<br>觉得Memory Leak倒不是大问题,因为Key所占内存不多,最主要是Key过多会影响检索的效率.<br>个人觉得解决这个问题应该很简单:</p>
<p>我们使用SoftReference,必然要在获取SoftReference后需要检查SoftReference.get()是否为空,<span style="color: #ff0000;">如果为空,就只有一种可能,在你没有手动Remove的情况下,虚拟机把它回收了,那么回收必然造成有一些无效的key,在这种情况下做一次清理就可以了</span>。<br>注意<span style="color: #ff0000;">SoftReference.get()==null不应该<strong><span style="color: #0000ff;">平凡</span></strong>出现</span>,除非虚拟机<span style="color: #0000ff;"><strong>平凡</strong></span>对SoftReference进行回收,如果这样,你或许要检查你程序了。</p>
<p>另外通过继承ConcurrentHashMap 重写put/get几个方法,应该可以把这些操作封装到子类中,还是让子类作为一个Map&lt;K, T&gt;来使用。</p>
<p>感觉直接使用ConcurrentHashMap&lt;String, SoftReference&gt;还是别扭。</p>
<p> </p>
</div>
<p>楼主,打拼音的吧。</p>
43 楼 solonote 2009-09-18  
<p>无测试无真相,上代码</p>
<p> </p>
<pre name="code" class="java">import static org.junit.Assert.*;

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

import org.junit.Test;

public class TestCase {

@Test
public void testWakeConcurrentMap() {
ReferenceMap wakeMap = new ReferenceMap(WeakReference.class);
String key = "abc";
Object value = new Object();
wakeMap.put(key, value);
value = null;
System.gc();

assertNull(wakeMap.get(key));
assertTrue(wakeMap.isEmpty());
}

@Test
public void testSoftConcurrentMap() {
ReferenceMap wakeMap = new ReferenceMap(SoftReference.class);
String key = "abc";
Object value = new Object();
wakeMap.put(key, value);
value = null;
System.gc();

assertNotNull(wakeMap.get(key));
assertFalse(wakeMap.isEmpty());
}
}
</pre>
<p> </p>
<p>下面是Concurrent的Map子类</p>
<pre name="code" class="java">import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

public class ReferenceMap extends ConcurrentHashMap {
private Class&lt;? extends Reference&gt; refClass;

public ReferenceMap(Class refClass) {
super();
this.refClass = refClass;
}

public Object put(Object key, Object value) {
Reference ref = null;
//这里有点尴尬,没有默认构造器,无法使用反射获得实例...
if (refClass == SoftReference.class) {
ref = new SoftReference(value);
} else if (refClass == WeakReference.class) {
ref = new WeakReference(value);
}

Reference result = (Reference) super.put(key, ref);
if (result != null) {
return result.get();
} else
return null;
}

@Override
public Object get(Object key) {
Reference ref = (Reference) super.get(key);
if (ref.get() == null) {
cleanOverdueKeys();
return null;
}
return ref.get();
}

@Override
public boolean containsKey(Object key) {
if (super.containsKey(key)) {
Reference ref = (Reference) super.get(key);
if (ref.get() == null) {
return false;
} else
return true;
} else
return false;
}

//清除那些没用的key
private void cleanOverdueKeys() {
ArrayList removeList = new ArrayList();
for (Object key : this.keySet()) {
if (!containsKey(key))
removeList.add(key);
}
for (Object key : removeList) {
this.remove(key);
}
}
}
</pre>
42 楼 solonote 2009-09-18  
<p>ConcurrentHashMap&lt;String, SoftReference&gt;<br>刚才自己捣鼓了一下,确实没有什么好办法可以保证当SoftReference被回收时,Key也自动被Remove.<br>觉得Memory Leak倒不是大问题,因为Key所占内存不多,最主要是Key过多会影响检索的效率.<br>个人觉得解决这个问题应该很简单:</p>
<p>我们使用SoftReference,必然要在获取SoftReference后需要检查SoftReference.get()是否为空,<span style="color: #ff0000;">如果为空,就只有一种可能,在你没有手动Remove的情况下,虚拟机把它回收了,那么回收必然造成有一些无效的key,在这种情况下做一次清理就可以了</span>。<br>注意<span style="color: #ff0000;">SoftReference.get()==null不应该频繁出现</span>,除非虚拟机频繁对SoftReference进行回收,如果这样,你或许要检查你程序了。</p>
<p>另外通过继承ConcurrentHashMap 重写put/get几个方法,应该可以把这些操作封装到子类中,还是让子类作为一个Map&lt;K, T&gt;来使用。</p>
<p>感觉直接使用ConcurrentHashMap&lt;String, SoftReference&gt;还是别扭。</p>
<p> </p>
41 楼 xajhzc 2009-09-17  
靠,糊涂了.这问题没搞明白.............
40 楼 jiyanliang 2009-07-20  
patrickyao1988 写道
今天才知道原来还有这么多种引用类型的啊。。。请问有关这方面的什么书讲的比较好呢?

thinking in java中介绍 对象的集合 时在介绍持有引用时提到了一些,但是很可惜,不是很详细。。。
39 楼 patrickyao1988 2009-07-20  
今天才知道原来还有这么多种引用类型的啊。。。请问有关这方面的什么书讲的比较好呢?
38 楼 unsid 2009-07-20  
java中为什么不提供这样的api,通过传入一个对象的引用,返回一个结构,结构中是:这个对象对应的类在系统中存在多少个实例,每个实例在进程中存在几个引用,引用的变量名是什么,在哪些线程中第几行引用(类似堆栈跟踪)
37 楼 flashing 2009-07-19  
<div class="quote_title">Feiing 写道</div>
<div class="quote_div">我还没看后面,第一个例子就是错的!
<p>而且这个例子的错误好几处,lz根本就没搞明白堆内存,栈内存以及引用和对象的关系这几个问题。</p>
<p>Object strongReference = referent;现在这俩ref都指向了new Object();产生的对象;</p>
<p>referent = null;lz认为这样strongReference 也会是null了????</p>
<p>System.gc();如果你明白什么是分代式回收以及图检索策略,就该明白这里是否会回收上面产生的对象!</p>
<p>这俩ref一直都在生命周期内,为什么要回收这个对象呢?</p>
<p>后面还没看,我03年的时候曾经仔细看过这四种ref的关系,但是除了高速缓存可能会用弱引用外,其他地方似乎很难用到啊。</p>
<p> </p>
<p> </p>
<p> </p>
<p><br></p>
<p> </p>
<p>理解 Java 的 GC 与 幽灵引用<br>  <br>         Java 中一共有 4 种类型的引用 : StrongReference、 SoftReference、 WeakReference 以及 PhantomReference (传说中的幽灵引用 呵呵), <br>这 4 种类型的引用与 GC 有着密切的关系,  让我们逐一来看它们的定义和使用场景 :<br><br>        1. Strong Reference<br>        <br>        StrongReference 是 Java 的默认引用实现,  它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时 GC 执行后将会被回收</p>
<pre name="code" class="java"> @Test
public void strongReference() {
Object referent = new Object();

/**
* 通过赋值创建 StrongReference
*/
Object strongReference = referent;

assertSame(referent, strongReference);

referent = null;
System.gc();

/**
* StrongReference 在 GC 后不会被回收
*/
assertNotNull(strongReference);
}
</pre>
<p><br><br><br><br>        2. WeakReference &amp; WeakHashMap<br><br>WeakReference, 顾名思义,  是一个弱引用,  当所引用的对象在 JVM 内不再有强引用时, GC 后 weak reference 将会被自动回收</p>
<pre name="code" class="java"> @Test
public void weakReference() {
Object referent = new Object();
WeakReference&lt;Object&gt; weakRerference = new WeakReference&lt;Object&gt;(referent);

assertSame(referent, weakRerference.get());

referent = null;
System.gc();

/**
* 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收
*/
assertNull(weakRerference.get());
}
</pre>
<p><br><br><br>WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry</p>
<pre name="code" class="java"> @Test
public void weakHashMap() throws InterruptedException {
Map&lt;Object, Object&gt; weakHashMap = new WeakHashMap&lt;Object, Object&gt;();
Object key = new Object();
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));
}
</pre>
<p><br><br><br>        3. SoftReference<br><br>SoftReference 于 WeakReference 的特性基本一致, 最大的区别在于 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用</p>
<pre name="code" class="java"> @Test
public void softReference() {
Object referent = new Object();
SoftReference&lt;Object&gt; softRerference = new SoftReference&lt;Object&gt;(referent);

assertNotNull(softRerference.get());

referent = null;
System.gc();

/**
*  soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用
*/
assertNotNull(softRerference.get());
}

</pre>
<p><br><br><br>        4. PhantomReference<br><br>        作为本文主角, Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同,  因为它的 get() 方法永远返回 null, 这也正是它名字的由来</p>
<pre name="code" class="java"> @Test
public void phantomReferenceAlwaysNull() {
Object referent = new Object();
PhantomReference&lt;Object&gt; phantomReference = new PhantomReference&lt;Object&gt;(referent, new ReferenceQueue&lt;Object&gt;());

/**
* phantom reference 的 get 方法永远返回 null
*/
assertNull(phantomReference.get());
}

</pre>
<p><br><br>         诸位可能要问, 一个永远返回 null 的 reference 要来何用,  请注意构造 PhantomReference 时的第二个参数 ReferenceQueue(事实上 WeakReference &amp; SoftReference 也可以有这个参数),<br>PhantomReference 唯一的用处就是跟踪 referent  何时被 enqueue 到 ReferenceQueue 中.<br><br>     5. RererenceQueue<br><br>当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作.   将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.</p>
<pre name="code" class="java"> @Test
public void referenceQueue() throws InterruptedException {
Object referent = new Object();
ReferenceQueue&lt;Object&gt; referenceQueue = new ReferenceQueue&lt;Object&gt;();
WeakReference&lt;Object&gt; weakReference = new WeakReference&lt;Object&gt;(referent, referenceQueue);

assertFalse(weakReference.isEnqueued());
Reference&lt;? extends Object&gt; polled = referenceQueue.poll();
assertNull(polled);

referent = null;
System.gc();

assertTrue(weakReference.isEnqueued());
Reference&lt;? extends Object&gt; removed = referenceQueue.remove();
assertNotNull(removed);
}
</pre>
<p><br><br>6.  PhantomReference  vs WeakReference<br><br>PhantomReference  有两个好处, 其一, 它可以让我们准确地知道对象何时被从内存中删除, 这个特性可以被用于一些特殊的需求中(例如 Distributed GC,  XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作). <br><br>其二, 它可以避免 finalization 带来的一些根本性问题, 上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中,  但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?<br>这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用,  这将导致这一轮的 GC 无法回收这个对象并有可能<br>引起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用,  也就不会出现上述问题,  当然这是一个很极端的例子, 一般不会出现. <br><br>7. 对比<br><br>taken from http://mindprod.com/jgloss/phantom.html</p>
<p><span style="font-family: 'tiresias pcfont z'; color: #421f00; font-size: 16px;">
<table class="standard" border="0">
<thead>
<tr>
<th style="font-family: calibri, 'bitstream vera sans', 'segoe ui', arial, helvetica, sans-serif; background-color: #95cbff; color: #000000; display: table-cell; font-size: 1.08em; font-weight: bold; padding: 5px;" colspan="5">Soft vs Weak vs Phantom References</th>
</tr>
<tr>
<th style="font-family: calibri, 'bitstream vera sans', 'segoe ui', arial, helvetica, sans-serif; background-color: #95cbff; color: #000000; display: table-cell; font-size: 1.08em; font-weight: bold; padding: 5px;">Type</th>
<th style="font-family: calibri, 'bitstream vera sans', 'segoe ui', arial, helvetica, sans-serif; background-color: #95cbff; color: #000000; display: table-cell; font-size: 1.08em; font-weight: bold; padding: 5px;">Purpose</th>
<th style="font-family: calibri, 'bitstream vera sans', 'segoe ui', arial, helvetica, sans-serif; background-color: #95cbff; color: #000000; display: table-cell; font-size: 1.08em; font-weight: bold; padding: 5px;">Use</th>
<th style="font-family: calibri, 'bitstream vera sans', 'segoe ui', arial, helvetica, sans-serif; background-color: #95cbff; color: #000000; display: table-cell; font-size: 1.08em; font-weight: bold; padding: 5px;">When GCed</th>
<th style="font-family: calibri, 'bitstream vera sans', 'segoe ui', arial, helvetica, sans-serif; background-color: #95cbff; color: #000000; display: table-cell; font-size: 1.08em; font-weight: bold; padding: 5px;">Implementing Class</th>
</tr>
</thead>
<tbody>
<tr>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Strong Reference</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">An ordinary reference. Keeps objects alive as long as they are referenced.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">normal reference.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Any object not pointed to can be reclaimed.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">default</td>
</tr>
<tr>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Soft Reference</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Keeps objects alive provided there’s enough memory.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">to keep objects alive even after clients have removed their references (memory-sensitive caches), in case clients start asking for them again by key.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">After a first gc pass, the JVM decides it still needs to reclaim more space.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">
<span class="package">java.lang.ref.</span><span class="class" style="background-color: transparent; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Lucida Console', 'Lucida Sans', 'Lucida Sans Unicode', Courier, 'Courier New', 'Segoe UI', Arial, monospace; color: #653510;">SoftReference</span> </td>
</tr>
<tr>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Weak Reference</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Keeps objects alive only while they’re in use (reachable) by clients.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Containers that automatically delete objects no longer in use.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">After gc determines the object is only weakly reachable</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">
<span class="package">java.lang.ref.</span><span class="class" style="background-color: transparent; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Lucida Console', 'Lucida Sans', 'Lucida Sans Unicode', Courier, 'Courier New', 'Segoe UI', Arial, monospace; color: #653510;">WeakReference</span> <br><span class="package">java.util.</span><span class="class" style="background-color: transparent; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Lucida Console', 'Lucida Sans', 'Lucida Sans Unicode', Courier, 'Courier New', 'Segoe UI', Arial, monospace; color: #653510;">WeakHashMap</span> </td>
</tr>
<tr>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Phantom Reference</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Lets you clean up after finalization but before the space is reclaimed (replaces or augments the use of<span class="method">finalize</span>())</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">Special clean up processing</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">After finalization.</td>
<td style="font-size: 1em; line-height: 1.2em; padding: 5px;">
<span class="package">java.lang.ref.</span><span class="class" style="background-color: transparent; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Lucida Console', 'Lucida Sans', 'Lucida Sans Unicode', Courier, 'Courier New', 'Segoe UI', Arial, monospace; color: #653510;">PhantomReference<br><br></span>
</td>
</tr>
</tbody>
</table></span><br>8. 小结<br>       一般的应用程序不会涉及到 Reference 编程, 但是了解这些知识会对理解 GC 的工作原理以及性能调优有一定帮助,   在实现一些基础性设施比如缓存时也可能会用到, 希望本文能有所帮助.<br><br>        王政 于 2009,6,3</p>
       
<p> </p>
</div>
<p> </p>
36 楼 moonjava 2009-07-02  
好帖         !
35 楼 zhang_xzhi_xjtu 2009-06-23  
关于对象再生和WeakReference和PhantomReference的区别
http://zhang-xzhi-xjtu.iteye.com/blog/413159
34 楼 taupo 2009-06-16  
ReferenceQueue 的作用是不是只要是在它里面的对象
当垃圾收集时一定会被当垃圾清除,不管这个对象是否还被其他引用???

33 楼 zfzone 2009-06-15  
taowen 写道
jenlp520 写道
taowen 写道
jenlp520 写道
taowen 写道
如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。


应该是这样 如果你Sofrreference里面引用的对象没有别的引用的时候 在每次内存溢出前就会被自动清理掉

Object -> SoftReference -> Map
SoftReference对Object的引用会自动断开。但是SoftReference本身还会在Map中存在一个Entry。如何做到内存溢出的时候自动把SoftReference从Map中移除呢?


你用softReference来做缓存主要目的是为了长期持有对象而不引起内存泄露 那个对象自然就是被softReference引用的
既然在内存泄露前 你的大对象已经被清理了 你的这次危机自然就解除了 等你下一次用这个大对象的时候 你会发现softReference的get()是null了 那么你是不是会重新put一个新的进去呢
于是你上次的softReference会在下次gc调用的时候被清理掉了...

如果这个key再也不被引用了,就永远不会被清除了。这算不算是Memory Leak呢?


针对以上各位说的问题,对于Map当中的key,可以做一个简单的修改,代码如下:
先有一个工具类,专用于获得key值的。
public class ToolUtil {
    //作为缓存的Map
    public static final ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
    public static ExpandSoftReference getKey(String key){
        return new ExpandSoftReference(new ExpandSring(key));
    }
    private static class ExpandSring{
        private String key;
        private ExpandSring() {
        }

        private ExpandSring(String key) {
            this.key = key;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        protected void finalize() throws Throwable {
            super.finalize();
            concurrentHashMap.remove(new ExpandSoftReference(this));
        }

        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            ExpandSring that = (ExpandSring) o;

            if (key != null ? !key.equals(that.key) : that.key != null) return false;

            return true;
        }

        public int hashCode() {
            return (key != null ? key.hashCode() : 0);
        }
    }
}
ExpandSoftReference类内容如下:
public class ExpandSoftReference extends SoftReference {
    private String relativelyStr;
    public ExpandSoftReference(Object referent) {
        super(referent);
        relativelyStr = referent.toString()+"_key";
    }

    public ExpandSoftReference(Object referent, ReferenceQueue q) {
        super(referent, q);
        relativelyStr = referent.toString()+"_key";
    }

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ExpandSoftReference that = (ExpandSoftReference) o;

        if (relativelyStr != null ? !relativelyStr.equals(that.relativelyStr) : that.relativelyStr != null)
            return false;

        return true;
    }

    public int hashCode() {
        return (relativelyStr != null ? relativelyStr.hashCode() : 0);
    }
}
我在使用时如下这样,便可以了
ToolUtil.concurrentHashMap.put(ToolUtil.getKey("snsky"),new Object());
此扩展有以下几个关键点所在
ExpandSring类扩展了
finalize方法
在ExpandSring类对象占用的内存被回收之前释放在Map当中对应key-value对
ExpandSoftReference类扩展了"软引用类"(SoftReference)
并且为了可以跟据key值查找到对应的value
此类当中用relativelyStr属性作为比较的值。在获取方法时可以用
ToolUtil.concurrentHashMap.get(ToolUtil.getKey("snsky"));
以上代表我个人的一些想法,希望能集思广义,看是否有不合理的地方。


32 楼 srdrm 2009-06-13  
比较深奥, 先标记一下吧
31 楼 tottiwei 2009-06-11  
taowen 写道
jenlp520 写道
taowen 写道
jenlp520 写道
taowen 写道
如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。


应该是这样 如果你Sofrreference里面引用的对象没有别的引用的时候 在每次内存溢出前就会被自动清理掉

Object -> SoftReference -> Map
SoftReference对Object的引用会自动断开。但是SoftReference本身还会在Map中存在一个Entry。如何做到内存溢出的时候自动把SoftReference从Map中移除呢?


你用softReference来做缓存主要目的是为了长期持有对象而不引起内存泄露 那个对象自然就是被softReference引用的
既然在内存泄露前 你的大对象已经被清理了 你的这次危机自然就解除了 等你下一次用这个大对象的时候 你会发现softReference的get()是null了 那么你是不是会重新put一个新的进去呢
于是你上次的softReference会在下次gc调用的时候被清理掉了...

如果这个key再也不被引用了,就永远不会被清除了。这算不算是Memory Leak呢?


不被清除的对象未必都是Memory Leak。
一般来说,缓存或者静态数据应该是贯穿整个application的生命周期(假设没有任何的缓存策略),此时key是被有意识的引用或者说是保存:Map(global or static)-Entry-Key。所以对于缓存中所有的数据,可不能说,用不到的就是Memory Leak。正如湖人打魔术,奥兰多混好了,就不回洛杉矶了,可洛杉矶的场馆咱照样得维护好了~~
30 楼 baggio_hk 2009-06-11  
请教下,有点晕
public void weakReference() {  
    Object referent = new Object();  
    WeakReference<Object> weakRerference = new WeakReference<Object>(referent);  
 
    assertSame(referent, weakRerference.get());  
      
    referent = null;  
    System.gc();  
      
    /** 
     * 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收 
     */ 
    assertNull(weakRerference.get());  


public void softReference() {  
    Object referent = new Object();  
    SoftReference<Object> softRerference = new SoftReference<Object>(referent);  
 
    assertNotNull(softRerference.get());  
      
    referent = null;  
    System.gc();  
      
    /** 
     *  soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用 
     */ 
    assertNotNull(softRerference.get());  
}  
代码差不多,只是SoftReference和WeakReference不同,引用效果就不同,能不能具体举例一下哪些是SoftReference,哪些是WeakReference

相关推荐

Global site tag (gtag.js) - Google Analytics