论坛首页 Java企业应用论坛

理解 Java 的 GC 与 幽灵引用

浏览 60004 次
该帖已经被评为精华帖
作者 正文
   发表时间:2009-06-04  
jenlp520 写道
taowen 写道
如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。


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

Object -> SoftReference -> Map
SoftReference对Object的引用会自动断开。但是SoftReference本身还会在Map中存在一个Entry。如何做到内存溢出的时候自动把SoftReference从Map中移除呢?
0 请登录后投票
   发表时间:2009-06-04   最后修改:2009-06-04
taowen 写道
如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。


应该用 ReferenceQueue 来实现, 可以参考 WeakHashMap 的实现, 另外缓存类型应该是  ConcurrentHashMap<SoftReference, Object>, 因为清除的依据肯定是 key 而非 value
0 请登录后投票
   发表时间: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调用的时候被清理掉了...
0 请登录后投票
   发表时间:2009-06-04  
Feiing 写道
taowen 写道
如果我用一个ConcurrentHashMap<String, SoftReference>来持有我的Cache。那么我是应该是Finalizer呢还是ReferenceQueue来把SoftReference从cache中清除出去?目的就是把缓存尽可能长的时间持有,除非内存不够了。


应该用 ReferenceQueue 来实现, 可以参考 WeakHashMap 的实现, 另外缓存类型应该是  ConcurrentHashMap<SoftReference, Object>, 因为清除的依据肯定是 key 而非 value

Key如果是SoftReference,那我怎么取我的cache啊?
0 请登录后投票
   发表时间: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呢?
0 请登录后投票
   发表时间:2009-06-04  
不错不错,又学会了点东西
0 请登录后投票
   发表时间: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;
       }

    }

}


0 请登录后投票
   发表时间:2009-06-05  
Cache必须基于ConcurrentHashMap吧。貌似你的getTable不能这么简单。。。
0 请登录后投票
   发表时间:2009-06-05  
恩,讲得很详细,有见地。

不过Java里面这么多种引用方式,对程序员来说还是比较复杂啊。

0 请登录后投票
   发表时间:2009-06-05  
taowen 写道
Cache必须基于ConcurrentHashMap吧。貌似你的getTable不能这么简单。。。


缓存基于线程安全上来说还是用ConcurrentHashMap 然后自己在把value用softreference包装下


对于你说那个如果key不在引用了 softreference就不会被清楚
我的想法是这样 如果softreference来说如果get为null那么显然对于这个缓存来说他是脏数据了
但是我想了想有2点
1:这个脏数据很小 如果数量很少的话几乎可以忽略不计 而且如果你在使用缓存的时候做了必要的判断 那么这个脏数据也完全不会破坏你的程序
2:这个脏数据应该很少,既然是用在缓存上 那么就应该是使用频率很大的 那么就象之前我说的

当然这2点也不能完全说明这个脏数据会影响程序的健壮 不过有利也有弊 还是自己权衡下在使用

ps:如果你愿意定时去遍历缓存然后清理也无妨....


0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics