锁定老帖子 主题:理解 Java 的 GC 与 幽灵引用
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间: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。正如湖人打魔术,奥兰多混好了,就不回洛杉矶了,可洛杉矶的场馆咱照样得维护好了~~ |
|
返回顶楼 | |
发表时间: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")); 以上代表我个人的一些想法,希望能集思广义,看是否有不合理的地方。 |
|
返回顶楼 | |
发表时间:2009-06-16
ReferenceQueue 的作用是不是只要是在它里面的对象
当垃圾收集时一定会被当垃圾清除,不管这个对象是否还被其他引用??? |
|
返回顶楼 | |
发表时间:2009-06-23
关于对象再生和WeakReference和PhantomReference的区别
http://zhang-xzhi-xjtu.iteye.com/blog/413159 |
|
返回顶楼 | |
发表时间:2009-07-20
java中为什么不提供这样的api,通过传入一个对象的引用,返回一个结构,结构中是:这个对象对应的类在系统中存在多少个实例,每个实例在进程中存在几个引用,引用的变量名是什么,在哪些线程中第几行引用(类似堆栈跟踪)
|
|
返回顶楼 | |
发表时间:2009-07-20
今天才知道原来还有这么多种引用类型的啊。。。请问有关这方面的什么书讲的比较好呢?
|
|
返回顶楼 | |
发表时间:2009-07-20
patrickyao1988 写道 今天才知道原来还有这么多种引用类型的啊。。。请问有关这方面的什么书讲的比较好呢?
thinking in java中介绍 对象的集合 时在介绍持有引用时提到了一些,但是很可惜,不是很详细。。。 |
|
返回顶楼 | |
发表时间:2009-09-17
最后修改:2009-09-18
靠,糊涂了.这问题没搞明白.............
|
|
返回顶楼 | |
发表时间:2009-09-18
最后修改:2009-09-18
ConcurrentHashMap<String, SoftReference> 我们使用SoftReference,必然要在获取SoftReference后需要检查SoftReference.get()是否为空,如果为空,就只有一种可能,在你没有手动Remove的情况下,虚拟机把它回收了,那么回收必然造成有一些无效的key,在这种情况下做一次清理就可以了。 另外通过继承ConcurrentHashMap 重写put/get几个方法,应该可以把这些操作封装到子类中,还是让子类作为一个Map<K, T>来使用。 感觉直接使用ConcurrentHashMap<String, SoftReference>还是别扭。
|
|
返回顶楼 | |
发表时间:2009-09-18
最后修改:2009-09-18
无测试无真相,上代码
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()); } }
下面是Concurrent的Map子类 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<? extends Reference> 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); } } } |
|
返回顶楼 | |