WeakHashMap源码分析
来源:互联网 发布:java 小游戏源代码 编辑:程序博客网 时间:2024/05/30 23:06
总结
WeakHashMap 实现了Map接口,但是它与HashMap的区别是 key为WeakReference类型,当key中的对象不再是强引用或者软引用时,垃圾收集器将key自动回收。
WeakHashMap支持null key和null value
WeakHashMap的应用场景
由于垃圾收集器回收一个对象时,通过对象可达性分析算法进行回收。如果我们创建了一个HashMap,HashMap中存放了对象。除非把HashMap对象回收掉,否则HashMap中的对象将不会被回收,根据对象可达性原则,因为HashMap中引用了此对象。在一个大的Hash表中,将会浪费内存。
应用例子:
JDK ThreadLocal 实现
具体的源码不在详细的分析。ThreadLocal是一个map结构,每个Thread对象都有一个ThreadLocalMap对象,用来存放此线程的本地变量,其中map中的key为ThreadLocal,value为需要存放的值。hash冲突使用线性探测的方式。
当本地创建的ThreadLoal的强引用不可达时,虚拟机在进行垃圾回收时把这些对象自动回收,然后放入Queue中,通过遍历queue移除map中key为null的entry,节约了不必要的内存浪费。/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
源码解读
WeakHashMap继承了抽象类AbstractMap 并实现了Map接口。
public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>
WeakHashMap实例字段及类字段
默认容量 16
private static final int DEFAULT_INITIAL_CAPACITY = 16;
最大容量 必须是2的幂 默认1<<30
private static final int MAXIMUM_CAPACITY = 1 << 30;
负载因子 默认值是0.75 (负载因子跟Hash冲突有关,因子越大,冲突就越大)
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
底层数据结构(数组+链表);数组长度必须是2的幂
Entry<K,V>[] table;
map中实际元素的数量
private int size;
当下次动态扩容时map中元素数量
private int threshold;
负载因子
private final float loadFactor;
虚引用队列。队列中存放被垃圾收集器回收的Entry中的key
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
记录map修改次数的字段(此字段主要用于标识在迭代map时,如果map结构被改变,那么它将出现fail-fast错误,抛出ConcurrentModificationException)
int modCount;
底层数据结构实体类(是一个链表结构)
/** * The entries in this hash table extend WeakReference, using its main ref * field as the key. */ 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的key为弱引用,value为强引用。在创建key的时候注册了ReferenceQueue,因此被垃圾回收key的存放到ReferenceQueue中。通过遍历Queue,把queue对应的key在map中删除。由于queue与map中的key是同一个对象,所以使用内存地址的比较,一定使用==
/** *实现思路: *1.从队列里poll()元素,直到队列为空 *2.hash 到table中对应的元素 *3.由于数组里的元素是一个链表结构。当需要删除的元素是头元素 *是,把table[i]设置为头元素的下一个元素。即table[i]=p *否则:直接删除。即pre.next =p.next。并把value设置为null */ private void expungeStaleEntries() { 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; while (p != null) { Entry<K,V> next = p.next; /*特别注意,此时的key为null,但是内存地址 * 没变,因此使用==比较。这里涉及到equals * 与==的区别。 * == 的用法:1.对于基本数据类型,是值比 * 较。对于引用对象类型,使用的是内存地址 * equals:引用对象的值比较。 */ if (p == e) { 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 e.value = null; // Help GC size--; break; } prev = p; p = next; } } } }
如何存放key为null的值
由于key为null代表着被垃圾收集器进行了回收。因此WeakHashMap中当key为null时,它实际上在集合中存放的是一个Object的空对象。
/** * Value representing null keys inside tables. */ private static final Object NULL_KEY = new Object();
put方法实现
先判断key是否为null,如果为null,则转变为Object.
然后进行依据key得到hash值,定位到table,由于table中的元素是一个链表结构。因此对链表进行遍历。
public V put(K key, V value) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] 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, value, queue, h, e); if (++size >= threshold) resize(tab.length * 2); return null; }
- 《Java源码分析》:WeakHashMap
- WeakHashMap源码分析
- 《Java源码分析》:WeakHashMap
- WeakHashMap源码分析
- jdk源码分析之WeakHashMap
- java-EnumMap、IdentityHashMap、WeakHashMap源码分析
- WeakHashMap实现原理及源码分析
- 基础数据机构之WeakHashMap源码分析
- WeakHashMap分析
- Java-Collection源码分析(九)——WeakHashMap
- WeakHashMap类源码解析
- Java8 - WeakHashMap源码
- Java WeakHashMap 源码解析
- Java8源码-WeakHashMap
- WeakHashMap源码解析
- WeakHashMap原理分析
- IdentityHashMap和WeakHashMap源码阅读
- 从源码理解WeakHashMap.java
- JavaSE_笔试题_单选选择题1
- 数据结构 队列以及Java代码实现
- 一个Redis消息队列实现
- 泛型算法
- Redis--事务
- WeakHashMap源码分析
- Linux操作系统故障排除-centos7口令丢失
- ubuntu16.04 安装composer和 laravel
- 前端JS知识要点总结(1)
- 数据结构 栈以及Java代码实现
- 缓存穿透和缓存失效的预防和解决
- 最长上升子序列1007
- USACO-Section2.1 Sorting a Three-Valued Sequence[排序]
- 史上最简单的 MySQL 教程(二十七)「连接查询(下)」