JDK8 源码角度分析 WeakHashMap 与 WeakReference 和 ReferenceQueue 三者关联

什么是 WeakReference

WeakReference 是弱引用。GC 时,JVM 可以随时回收。WeakReference 可以获取对象本身(虚引用不可以获取对象本身)也可以跟踪对象 GC。

Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mappings.
Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object and all weak references to any other weakly-reachable objects from which that object is reachable through a chain of strong and soft references. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable. At the same time or at some later time it will enqueue those newly-cleared weak references that are registered with reference queues.

弱引用对象,它不阻止它们的引用对象被设定为 finalizable、finalized,然后被回收。弱引用最常用于实现规范化映射。
假设垃圾收集器在某个时间点确定一个对象是弱可达的。此时,它将自动清除对该对象的所有弱引用,以及对任何其他弱可达对象的所有弱引用,而该对象可通过强和软引用链访问。同时,它将声明所有以前弱可达的对象为 finalizable。在同一时间或稍后的时间,它将对那些新清除的已注册到引用队列中的弱引用进行排队。

public class WeakReference<T> extends Reference<T> {

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

ReferenceQueue

ReferenceQueue 一般会和 WeakReference 配合使用。举个例子,当对象 obj 被回收以后,WeakReference 对象会被放入 ReferenceQueue 中:

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class Test1 {
    public static void main(String[] args) {
        Object obj = new Object();
        WeakReference reference = new WeakReference(obj, new ReferenceQueue());
    }
}

WeakHashMap 中就是类型为 Object 的 K 被回收后,会将 Entry 对象放入 ReferenceQueue 中。

WeakHashMap

  • Entry 继承自 WeakReference
  • WeakHashMap 中声明了全局 ReferenceQueue
  • Entry 的 Value 作为 Key 的伴随数据。伴随的 key 而生,伴随 key 而死。
    • 当 Entry 的 key 被 GC 时,该 Entry 会被放入 ReferenceQueue 中。下一次 put 或 get 时,Entry 从 ReferenceQueue 出队,然后根据 Entry 的 hash 值计算其在 table 中的位置,并移除。

Entry 继承自 WeakReference

    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        Entry<K,V> next;

        /**
         * Creates new entry.
         * 构造方法
         */
        Entry(Object key, V value,
              ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }
    }

WeakHashMap 中声明了全局 ReferenceQueue

    /**
     * Reference queue for cleared WeakEntries
     * Reference queue 用于保存已经 key 已经被回收了的 Entry
     */
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();

put 和 get 时回收 WeakEntries

put 和 get 方法都会调用 expungeStaleEntries 方法来删除 table 中 key 已经被回收的 Entry。

put
    public V put(K key, V value) {
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable(); // 内部调用 expungeStaleEntries
        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, value, queue, h, e);
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }
get
    public V get(Object key) {
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable(); // 内部调用 expungeStaleEntries
        int index = indexFor(h, tab.length);
        Entry<K,V> e = tab[index];
        while (e != null) {
            if (e.hash == h && eq(k, e.get()))
                return e.value;
            e = e.next;
        }
        return null;
    }
getTable
    /**
     * Returns the table after first expunging stale entries.
     * 在第一次删除过期 entries 后返回 table。
     */
    private Entry<K,V>[] getTable() {
        expungeStaleEntries(); 
        return table;
    }
expungeStaleEntries
    /**
     * Expunges stale entries from the table.
     * 从 table 中删除过期的 entries
     */
    private void expungeStaleEntries() {
        for (Object x; (x = queue.poll()) != null; ) {
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>) x; // 将 x 强转为 Entry e
                int i = indexFor(e.hash, table.length); // e 在 table 中的下标

                // prev 和 p 的初始值都是 prev
                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                while (p != null) {
                    Entry<K,V> next = p.next;
                    if (p == e) { // 找到 e 了,从 table 中删除之
                        if (prev == e)
                            table[i] = next;
                        else
                            prev.next = next;
                        // Must not null out e.next;
                        // stale entries may be in use by a HashIterator
                        // 过期的 entries 可能正在被 HashIterator 使用,所以 e.next 不能设置为 null
                        e.value = null; // Help GC
                        size--;
                        break;
                    }
                    prev = p;
                    p = next;
                }
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值