博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从源码角度分析 WeakHashMap 垃圾回收原理
阅读量:3920 次
发布时间:2019-05-23

本文共 3165 字,大约阅读时间需要 10 分钟。

公众号后台回复“资料

获取作者独家秘制学习资料

本文来源:涤生的博客

介绍

WeakHashMap自然联想到的是HashMap。确实,WeakHashMap与HashMap一样是个散列表,存储内容也是键值对。

这里与HashMap类似的功能就不展开了,本文重点关注在WeakHashMap是如何做到回收数据?

垃圾回收原理

谈WeakHashMap回收原理得从WeakReference(弱引用)说起。

大家都知道GC回收对象前提是,从根集出发的引用中没有有效引用指向该对象,则该对象就可以被回收。

这里的有效引用并不包含WeakReference,虽然弱引用可以用来访问对象,但进行垃圾回收时弱引用并不会被考虑在内,仅有弱引用指向的对象仍然会被GC回收。

那WeakHashMap是如何跟WeakReference关联起来的呢?

我们一起看看实现的code吧。

 
  1. /**

  2.     * The entries in this hash table extend WeakReference, using its main ref

  3.     * field as the key.

  4.     */

  5.    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {

  6.        V value;

  7.        int hash;

  8.        Entry<K,V> next;

  9.        /**

  10.         * Creates new entry.

  11.         */

  12.        Entry(Object key, V value,

  13.              ReferenceQueue<Object> queue,

  14.              int hash, Entry<K,V> next) {

  15.            super(key, queue);

  16.            this.value = value;

  17.            this.hash  = hash;

  18.            this.next  = next;

  19.        }

大家都知道HashMap实现里面有个Entry数组,WeakHashMap也一样也有一个Entry数组,但是此Entry与彼Entry有些不一样。

WeakHashMap的Entry是继承WeakReference,这样一来,整个Entry就是一个WeakReference。

再来看看Entry的构造方法,调用了super(key, queue),也就是调用了这个构造方法

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

  2.    /**

  3.     * Creates a new weak reference that refers to the given object.  The new

  4.     * reference is not registered with any queue.

  5.     *

  6.     * @param referent object the new weak reference will refer to

  7.     */

  8.    public WeakReference(T referent) {

  9.        super(referent);

  10.    }

  11.    /**

  12.     * Creates a new weak reference that refers to the given object and is

  13.     * registered with the given queue.

  14.     *

  15.     * @param referent object the new weak reference will refer to

  16.     * @param q the queue with which the reference is to be registered,

  17.     *          or null if registration is not required

  18.     */

  19.    public WeakReference(T referent, ReferenceQueue<? super T> q) {

  20.        super(referent, q);

  21.    }

  22. }

有两个参数,一个key,一个是queue,这个key就是WeakHashMap中存储的key值,这个queue是WeakHashMap中创建的ReferenceQueue。

 // Reference queue for cleared WeakEntries      private final ReferenceQueue queue = new ReferenceQueue<>();

那这个ReferenceQueue是干嘛的呢?

了解GC的朋友可能知道,当GC某个对象时,如果有此对象上还有弱引用与其关联,会将WeakReference对象与Reference类的pending引用关联起来,然后由Reference Handler线程将该插入ReferenceQueue队列。

也就是说当Entry中的key被GC时,会将Entry放入到ReferenceQueue中,WeakHashMap就能个通过ReferenceQueue中的Entry了解到哪些key已经被GC,或者即将马上被GC,起到了通知的作用。 

了解了以上信息后,我们再看下面这段代码:

 
  1. /**

  2.     * Expunges stale entries from the table.

  3.     */

  4.    private void expungeStaleEntries() {

  5.        for (Object x; (x = queue.poll()) != null; ) {

  6.            synchronized (queue) {

  7.                @SuppressWarnings("unchecked")

                  Entry<K,V> e = (Entry<K,V>) x;

  1.                int i = indexFor(e.hash, table.length);

  2.                Entry<K,V> prev = table[i];

  3.                Entry<K,V> p = prev;

  4.                while (p != null) {

  5.                    Entry<K,V> next = p.next;

  6.                    if (p == e) {

  7.                        if (prev == e)

  8.                            table[i] = next;

  9.                        else

  10.                            prev.next = next;

  11.                        // Must not null out e.next;

  12.                        // stale entries may be in use by a HashIterator

  13.                        e.value = null; // Help GC

  14.                        size--;

  15.                        break;

  16.                    }

  17.                    prev = p;

  18.                    p = next;

  19.                }

  20.            }

  21.        }

  22.    }

这段代码就是WeakHashMap用来处理ReferenceQueue中被GC的key所关联的Entry相关数据。

通过从queue中poll出相关的Entry,然后去WeakHashMap的entry数组中找到索引,然后从对应的链中去掉相关的Entry,最后将value赋值为空(Help GC),到这里就完成了相关数据的清理。

但是谁来触发expungeStaleEntries方法呢?有多个方法都可以触发,如put、get、remove、size等方法都能够触发相关的逻辑。

误区

是不是使用了WeakHashMap就一定没有问题了呢?

当然不是,如果没有触发expungeStaleEntries这个方法依然会导致内存泄漏。

比如初始化好WeakHashMap中相关数据后,一直不调用put、get、remove、size等相关方法,也是不能够正常回收的。

END

640?wx_fmt=png

推荐阅读

更多文章:

640?wx_fmt=png

欢迎长按下图关注公众号石杉的架构笔记,后台回复“资料”,获取作者独家秘制学习资料!

640?wx_fmt=jpeg

BAT架构经验倾囊相授

640?wx_fmt=gif

转载地址:http://murrn.baihongyu.com/

你可能感兴趣的文章
剑指 Offer 06. 从尾到头打印链表
查看>>
模式8.外观模式-Java
查看>>
模式9.建造者模式-Java
查看>>
模式11. 抽象工厂模式-Java
查看>>
模式10. 观察者模式-Java
查看>>
剑指 Offer 11. 旋转数组的最小数字
查看>>
剑指 Offer 07. 重建二叉树
查看>>
剑指 Offer 09. 用两个栈实现队列
查看>>
模式12.状态模式-Java
查看>>
Volatile-1.保证可见性
查看>>
Volatile-2.不保证原子性
查看>>
Volatile-3.禁止指令重排
查看>>
JMM (Java内存模型)
查看>>
剑指 Offer 13. 机器人的运动范围
查看>>
Leetcode 43. 字符串相乘
查看>>
剑指 Offer 24. 反转链表
查看>>
剑指 Offer 25. 合并两个排序的链表
查看>>
剑指 Offer 26. 树的子结构
查看>>
剑指 Offer 27. 二叉树的镜像
查看>>
剑指 Offer 28. 对称的二叉树
查看>>