util包源码(七):WeakHashMap源码笔记

阅读时间:2019.1.28

一、概述

WeakHashMap 继承于AbstractMap,实现了Map接口。和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。
与HashMap不同的是,WeakHashMap 桶内一直是单链表存储,不进行红黑树转化。
最后,WeakHashMap 最关键的特性:key是弱键。

特征 举例
强引用 任何时候都不会被垃圾回收器回收,如果内存不足,宁愿抛出OutOfMemoryError Object obj = new Object()
软引用 只有在内存将满的时候才会被垃圾回收器回收,如果还有可用内存,垃圾回收器不会回收 SoftReference sr = new SoftReference(new String(“hello”));
弱引用 只要垃圾回收器运行,就肯定会被回收,不管还有没有可用内存 WeakReference sr = new WeakReference(new String(“hello”));
虚引用 等于没有引用,任何时候都有可能被垃圾回收 PhantomReference pr = new PhantomReference(new String(“hello”), queue);

所有,当WeakHashMap 中某个“弱键”不再被其它对象引用,在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。

二、主要属性和方法

实现弱引用的关键有两个,一个是特殊的Entry,一个是ReferenceQueue的 queue

Entry<K,V>[] table;
private int threshold;
private final float loadFactor;
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();//存放下次对map操作时,需要清除掉的节点
private static final Object NULL_KEY = new Object();

WeakHashMap 的节点是继承WeakReference的,所以从他的定义就是一个弱引用的,从而实现“当没有外部变量指向该节点时,就会把该节点放到队列中,等待下次GC是删除掉”

    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);//初始化时,因为还没有别人来引用这个节点,就放到queue中,下次操作map时		     
                              //若还没有别人引用它,就把这个节点置空,以便GC
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }

        public boolean equals(Object o) {
		 	//...省略代码	
        }

        public int hashCode() {
 			//...省略代码
        }

        public String toString() {
            //...省略代码
        }
    }

WeakHashMap是主要通过expungeStaleEntries函数的来实现移除其内部不用的entry从而达到的自动释放内存的目的。所有的WeakHashMap操作都会先进行移除不使用节点的方法

    private Entry<K,V>[] getTable() {
        expungeStaleEntries();
        return table;
    }

在这里插入图片描述
具体分析expungeStaleEntries方法:
这个方法并不复杂:
第一步:先遍历queue,把queue中的节点在找到hashMap中找到
第二步:把这个节点的prev和next连接起来,把该节点=null

   private void expungeStaleEntries() {
   		//第一步:先遍历queue,把queue中的节点在找到hashMap中找到
        for (Object x; (x = queue.poll()) != null; ) {
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>) x;              
                int i = indexFor(e.hash, table.length);
                
                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                //第二步:把这个节点的prev和next连接起来,把该节点=null
                while (p != null) {
                    Entry<K,V> next = p.next;
                    if (p == e) {
                        if (prev == e)
                            table[i] = next;
                        else
                            prev.next = next;
                        e.value = null; // Help GC
                        size--;
                        break;
                    }
                    prev = p;
                    p = next;
                }
            }
        }
    }

三、总结

初始化方法,扩容方法,操作方法斗鱼hashmap没有太大区别,所以不再复述。
通过对expungeStaleEntries方法学习,我们可以知道WeakHashMap的工作流程:
1)初始化,把新建的entry放到queue中;
2)每次对map进行操作时,先同步的检查queue中的是否有节点,有节点,说明这个节点没有被外部变量引用。该节点就会被置为null。
3)在下次GC时,便会把他删除掉

猜你喜欢

转载自blog.csdn.net/jh19900712/article/details/86677244