锁定老帖子 主题:理解 Java 的 GC 与 幽灵引用
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-06-04
jenlp520 写道 taowen 写道 如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。
应该是这样 如果你Sofrreference里面引用的对象没有别的引用的时候 在每次内存溢出前就会被自动清理掉 Object -> SoftReference -> Map SoftReference对Object的引用会自动断开。但是SoftReference本身还会在Map中存在一个Entry。如何做到内存溢出的时候自动把SoftReference从Map中移除呢? |
|
返回顶楼 | |
发表时间:2009-06-04
最后修改:2009-06-04
taowen 写道 如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。
应该用 ReferenceQueue 来实现, 可以参考 WeakHashMap 的实现, 另外缓存类型应该是 ConcurrentHashMap<SoftReference, Object>, 因为清除的依据肯定是 key 而非 value |
|
返回顶楼 | |
发表时间:2009-06-04
最后修改:2009-06-04
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调用的时候被清理掉了... |
|
返回顶楼 | |
发表时间:2009-06-04
Feiing 写道 taowen 写道 如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。
应该用 ReferenceQueue 来实现, 可以参考 WeakHashMap 的实现, 另外缓存类型应该是 ConcurrentHashMap<SoftReference, Object>, 因为清除的依据肯定是 key 而非 value Key如果是SoftReference,那我怎么取我的cache啊? |
|
返回顶楼 | |
发表时间:2009-06-04
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呢? |
|
返回顶楼 | |
发表时间:2009-06-04
不错不错,又学会了点东西
|
|
返回顶楼 | |
发表时间:2009-06-04
最后修改:2009-06-04
taowen 写道 Feiing 写道 taowen 写道 如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。
应该用 ReferenceQueue 来实现, 可以参考 WeakHashMap 的实现, 另外缓存类型应该是 ConcurrentHashMap<SoftReference, Object>, 因为清除的依据肯定是 key 而非 value Key如果是SoftReference,那我怎么取我的cache啊? 说错了, 应该是 key 应该被包装成 SoftReference, 代码大致会是这样 (事实上 WeakHashMap 就是这样实现的) public class SoftHashMap<K, V> { /** * 用于清理无效 entries */ private final ReferenceQueue<K> queue = new ReferenceQueue<K>(); public V put(K key, V value) { ... new SoftEntry(key, value, queue); ... } public V get(K key) { int h = HashMap.hash(key.hashCode()); Entry[] tab = getTable(); int index = indexFor(h, tab.length); Entry<K,V> e = tab[index]; while (e != null) { if (e.hash == h && eq(key, e.get())) return e.value; e = e.next; } return null; } /** * Returns the table after first expunging stale entries. */ private Entry[] getTable() { expungeStaleEntries(); return table; } /** * 清理无用 entries */ private void expungeStaleEntries() { Entry<K,V> e; while ( (e = (Entry<K,V>) queue.poll()) != null) { remove e; } } private static final class SoftEntry<K, V> extends SoftReference<K> implements Map.Entry<K, V> { private V value; public SoftEntry(K key, V value, ReferenceQueue<K> queue) { super(key, queue); this.value = value; } public K getKey() { return get(); } public V getValue() { return this.value; } } } |
|
返回顶楼 | |
发表时间:2009-06-05
Cache必须基于ConcurrentHashMap吧。貌似你的getTable不能这么简单。。。
|
|
返回顶楼 | |
发表时间:2009-06-05
恩,讲得很详细,有见地。
不过Java里面这么多种引用方式,对程序员来说还是比较复杂啊。 |
|
返回顶楼 | |
发表时间:2009-06-05
taowen 写道 Cache必须基于ConcurrentHashMap吧。貌似你的getTable不能这么简单。。。
缓存基于线程安全上来说还是用ConcurrentHashMap 然后自己在把value用softreference包装下 对于你说那个如果key不在引用了 softreference就不会被清楚 我的想法是这样 如果softreference来说如果get为null那么显然对于这个缓存来说他是脏数据了 但是我想了想有2点 1:这个脏数据很小 如果数量很少的话几乎可以忽略不计 而且如果你在使用缓存的时候做了必要的判断 那么这个脏数据也完全不会破坏你的程序 2:这个脏数据应该很少,既然是用在缓存上 那么就应该是使用频率很大的 那么就象之前我说的 当然这2点也不能完全说明这个脏数据会影响程序的健壮 不过有利也有弊 还是自己权衡下在使用 ps:如果你愿意定时去遍历缓存然后清理也无妨.... |
|
返回顶楼 | |