认识 WeakHashMap !

弱引用:

    public static void WeakReferenceTest(){
        /**
         * 
弱引用的对象只能生存到下一次GC之前!
         */
        String str = new String("avd");
        WeakReference<String> wr = new WeakReference<String>(str);
        str = null; //消除强引用
        System.out.println("垃圾回收之前:"+wr.get());
        System.gc();//垃圾回收
        System.out.println("垃圾回收之后:"+wr.get());
    }

执行结果:
垃圾回收之前:avd
垃圾回收之后:null


为什么要使用WeakHashMap?

推荐博文!来源:互联网。
http://www.xiaoyaochong.net/wordpress/index.php/2013/08/05/java%e5%86%85%e5%ad%98%e6%b3%84%e9%9c%b2%e4%b8%8eweakhashmap/
Google Guava Cache 就是通过清理弱引用来回收内存的。


WeakHashMap 分析:

public class WeakHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>
成员变量
    /**
     * The default initial capacity -- MUST be a power of two.
     */

    // 默认加载因子16,必须为2的幂
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    /**
     * The maximum capacity, used if a higher value is implicitly specified
     * by either of the constructors with arguments.
     * MUST be a power of two <= 1<<30.
     */

    //  Entry[]最大长度 
    private static final int MAXIMUM_CAPACITY = 1 << 30;
    /**
     * The load fast used when none specified in constructor.
     */

    //默认加载因子
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */

    private Entry[] table;
    /**
     * The number of key-value mappings contained in this weak hash map.
     */

    // map中包含映射关系的数量
    private int size;
    /**
     * The next size value at which to resize (capacity * load factor).
     */

    // 当size的值不小于threshold时,需扩展table,并重新计算映射关系的存储位置,默认16*0.75=12
    private int threshold;
    /**
     * The load factor for the hash table.
     */

    //实际加载因子
    private final float loadFactor;
    /**
     * Reference queue for cleared WeakEntries
     */

    //引用队列,每一个弱引用entry须关联此队列,每次GC的弱引用entry会被添加到该队列
    private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
    /**
     * The number of times this WeakHashMap has been structurally modified.
     * Structural modifications are those that change the number of
     * mappings in the map or otherwise modify its internal structure
     * (e.g., rehash).  This field is used to make iterators on
     * Collection-views of the map 
fail-fast.
     *
     * @see ConcurrentModificationException
     */

    private volatile int modCount;
方法列表:
public Object get(Object arg-0); 
public Object put(Object arg-0,Object arg-1); 
public Collection values(); 
public void clear(); 
public boolean isEmpty(); 
public Set entrySet(); 
public void putAll(Map arg-0); 
public int size(); 
public Object remove(Object arg-0); 
public Set keySet(); 
public boolean containsKey(Object arg-0); 
public boolean containsValue(Object arg-0); 
public boolean equals(Object arg-0); 
public String toString(); 
public int hashCode(); 


成员变量:  
  /**
     * Value representing null keys inside tables.
     */

    // key为null时,用NULL_KEY取代
    private static final Object NULL_KEY = new Object();


public Object put(Object arg-0,Object arg-1); 
    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for this key, the old
     * value is replaced.
     */

    public V put(K key, V value) {
        K k = (K) maskNull(key);//如果key == null,则key用NULL_KEY代替
        int h = HashMap.hash(k.hashCode());
        Entry[] tab = getTable();//清理map中无效的引用
        int i = indexFor(h, tab.length);
        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }
        modCount++;
    Entry<K,V> e = tab[i];
        tab[i] = new Entry<K,V>(k, value, queue, h, e);//将新的元素添加到哈希表
        if (++size >= threshold)
            resize(tab.length * 2);//扩展Entry[]到原来的2倍
        return null;
    }

    /**
     * Use NULL_KEY for key if it is null.
     */

    private static Object maskNull(Object key) {
        return (key == null ? NULL_KEY : key);
    }
    /**
     * Applies a supplemental hash function to a given hashCode, which
     * defends against poor quality hash functions.  This is critical
     * because HashMap uses power-of-two length hash tables, that
     * otherwise encounter collisions for hashCodes that do not differ
     * in lower bits. Note: Null keys always map to hash 0, thus index 0.
     */

    // hash 函数
    static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20^ (h >>> 12);
        return h ^ (h >>> 7^ (h >>> 4);
    }
    /**
     * Returns index for hash code h.
     */

    // 重新计算entry的桶的位置
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

    /**
     * Rehashes the contents of this map into a new array with a
     * larger capacity.  This method is called automatically when the
     * number of keys in this map reaches its threshold.
     *
     * If current capacity is MAXIMUM_CAPACITY, this method does not
     * resize the map, but sets threshold to Integer.MAX_VALUE.
     * This has the effect of preventing future calls.
     */

    //扩展Entry[]的容量
    void resize(int newCapacity) {
        Entry[] oldTable = getTable();
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        Entry[] newTable = new Entry[newCapacity];
        transfer(oldTable, newTable);
        table = newTable;
        /*
         * If ignoring null elements and processing ref queue caused massive
         * shrinkage, then restore old table.  This should be rare, but avoids
         * unbounded expansion of garbage-filled tables.
         */

        //扩展容量之后,剩余元素很少,则收缩哈希表的容量到上一次未扩容是的大小,减少内存占用。
        if (size >= threshold / 2) {
            threshold = (int)(newCapacity * loadFactor);
        } else {
            expungeStaleEntries();
            transfer(newTable, oldTable);
            table = oldTable;
        }
    }
    /** Transfers all entries from src to dest tables */
    //将旧哈希表中的映射关系添加到新哈希表中
    private void transfer(Entry[] src, Entry[] dest) {
        for (int j = 0; j < src.length; ++j) {
            Entry<K,V> e = src[j];
            src[j] = null;
            while (e != null) {
                Entry<K,V> next = e.next; // e的下一个entry
                Object key = e.get(); // T get():返回注册的引用对象,如果此对象已回收,将返回null
                if (key == null) { // key == null, 解除对下一个entry 和value的引用
                    e.next = null;  // Help GC
                    e.value = null; //  "   "
                    size--;//不放到新哈希表中,所以其size应-1
                } else {
                    int i = indexFor(e.hash, dest.length);
                    e.next = dest[i];
                    dest[i] = e;
                }
                e = next;//e指向下一个entry
            }
        }
    }
    /**
     * Returns the table after first expunging stale entries.
     */

    //清除过时无效的引用
    private Entry[] getTable() {
        expungeStaleEntries();
        return table;
    }
    /**
     * Expunges stale entries from the table.
     */

    private void expungeStaleEntries() {
    Entry<K,V> e;
        while ( (e = (Entry<K,V>) queue.poll()) != null) {//轮询此队列 ,再遍历对应的桶
            int h = e.hash;
            int i = indexFor(h, table.length);
            Entry<K,V> prev = table[i];//对应桶的第一个元素
            Entry<K,V> p = prev;
            while (p != null) {
                Entry<K,V> next = p.next;//指向下一个元素
                if (p == e) { //p == e 说明该对象存在于队列之中,说明已经被垃圾回收器回收。
                    if (prev == e)//prev == e说明桶的第一个元素被回收
                        table[i] = next;//table[i]存放其下一个元素,prev和p指向next
                    else
                        prev.next = next;//prev越过p指向p.next
                    //解除引用,结束循环
                    e.next = null;  // Help GC
                    e.value = null; //  "   "
                    size--;
                    break;
                }
                //说明元素p未被回收
                prev = p;
                p = next;//使p指向下一个元素
            }
        }
    }
第一个元素被回收的情况:
初始状态:
第一次循环:移除table[i]位置的元素,下一个元素置于table[i]位置,该桶的循环结束

第一个元素不被回收的情况:
第一次循环:
第二次循环移除第二个元素:这个桶的循环结束


    /**
     * The entries in this hash table extend WeakReference, using its main ref
     * field as the key.
     */
    //哈希表中存储 的弱引用对象,最终继承自 Reference 抽象类。
    private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
        private V value;
        private final int hash;
        private Entry<K,V> next;
        /**
         * Creates new entry.
         */

        Entry(K key, V value,
          ReferenceQueue<K> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }
        public K getKey() {
            return WeakHashMap.<K>unmaskNull(get());//获取当前映射关系的key.
        }
        public V getValue() {
            return value;
        }
        public V setValue(V newValue) {
        V oldValue = value;
            value = newValue;
            return oldValue;
        }
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }
        public int hashCode() {
            Object k = getKey();
            Object v = getValue();
            return  ((k==null ? 0 : k.hashCode()) ^
                     (v==null ? 0 : v.hashCode()));
        }
        public String toString() {
            return getKey() + "=" + getValue();
        }
    }

    /**
     * Returns internal representation of null key back to caller as null.
     */

    //返回映射关系的key,若key为NULL_KEY,则转换为null. 
    private static <K> K unmaskNull(Object key) {
        return (K) (key == NULL_KEY ? null : key);
    }


ReferenceQueue以及Reference在WeakHashMap中的运用!

当添加一个映射关系时,将key及queue关联到Reference!
   
    /**
     * Reference queue for cleared WeakEntries
     */

    private final ReferenceQueue<K> queue = new ReferenceQueue<K>();

    public V put(K key, V value) {
        K k = (K) maskNull(key);
        int h = HashMap.hash(k.hashCode());
        Entry[] tab = getTable();
        int i = indexFor(h, tab.length);
        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }
        modCount++;
    Entry<K,V> e = tab[i];
        tab[i] = new Entry<K,V>(k, value, queue, h, e);
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }

    private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
        private V value;
        private final int hash;
        private Entry<K,V> next;
        /**
         * Creates new entry.
         */

        Entry(K key, V value,
          ReferenceQueue<K> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }
        public K getKey() {
            return WeakHashMap.<K>unmaskNull(get());
        }
        public V getValue() {
            return value;
        }
        public V setValue(V newValue) {
        V oldValue = value;
            value = newValue;
            return oldValue;
        }
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }
        public int hashCode() {
            Object k = getKey();
            Object v = getValue();
            return  ((k==null ? 0 : k.hashCode()) ^
                     (v==null ? 0 : v.hashCode()));
        }
        public String toString() {
            return getKey() + "=" + getValue();
        }
    }

    /**
     * Creates a new weak reference that refers to the given object and is
     * registered with the given queue.
     */

    public WeakReference(T referent, ReferenceQueue<? super T> q) {
    super(referent, q);
    }


    private T referent;        /* Treated specially by GC */

    ReferenceQueue<? super T> queue;

    Reference(T referent, ReferenceQueue<? super T> queue) {
    this.referent = referent;
    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }
当垃圾回收时:回收弱引用中的key
    Reference的内部类线程被调用,将被回收的弱引用对象添加到队列

    private static class ReferenceHandler extends Thread {
    ReferenceHandler(ThreadGroup g, String name) {
        super(g, name);
    }
    public void run() {
        for (;;) {
        Reference r;
        synchronized (lock) {
            if (pending != null) {
            r = pending;
            Reference rn = r.next;
            pending = (rn == r) ? null : rn;
            r.next = r;
            } else {
            try {
                lock.wait();
            } catch (InterruptedException x) { }
            continue;
            }
        }
        // Fast path for cleaners
        if (r instanceof Cleaner) {
            ((Cleaner)r).clean();
            continue;
        }
        ReferenceQueue q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        }
    }
    }

    private volatile Reference<? extends T> head = null;
    private long queueLength = 0;
    boolean enqueue(Reference<? extends T> r) {    /* Called only by Reference class */
    synchronized (r) {
        if (r.queue == ENQUEUED) return false;
        synchronized (lock) {
        r.queue = ENQUEUED;
        r.next = (head == null) ? r : head;
        head = r;

        queueLength++;
                if (r instanceof FinalReference) {
                    sun.misc.VM.addFinalRefCount(1);
                }
        lock.notifyAll();
        return true;
        }
    }
    }

toString()也调用expungeStaleEntries();
    public String toString() {
    Iterator<Entry<K,V>> i = entrySet().iterator();
    if (! i.hasNext())
        return "{}";
    StringBuilder sb = new StringBuilder();
    sb.append('{');
    for (;;) {
        Entry<K,V> e = i.next();
        K key = e.getKey();
        V value = e.getValue();
        sb.append(key   == this ? "(this Map)" : key);
        sb.append('=');
        sb.append(value == this ? "(this Map)" : value);
        if (! i.hasNext())
        return sb.append('}').toString();
        sb.append(", ");
    }
    }
    private abstract class HashIterator<T> implements Iterator<T> {
        int index;
        HashIterator() {
            index = (size() != 0 ? table.length : 0);
        }
    }
    /**
     * Returns the number of key-value mappings in this map.
     * This result is a snapshot, and may not reflect unprocessed
     * entries that will be removed before next attempted access
     * because they are no longer referenced.
     */

    public int size() {
        if (size == 0)
            return 0;
        expungeStaleEntries();
        return size;
    }
★★★★★★★★★★
WeakHashMap并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象。
继承自WeakReference的Entry对象才是所说的弱引用对象。
private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> 
弱引用对象中的key在GC时被回收:如下图可知key已经被回收,但entry还存在,value存在于entry内部。
GC之后,再次访问WeakHashMap,调用private void expungeStaleEntries()方法时清除entry和value对象。

推荐博客:
来源:互联网。http://www.cnblogs.com/redcreen/archive/2011/02/15/1955289.html
















posted @ 2014-02-16 15:51  龍變  阅读(257)  评论(0编辑  收藏  举报