【集合系列】- 深入浅出的分析 WeakHashMap
一、摘要
在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties 等等。
本文主要从数据结构和算法层面,探讨 WeakHashMap 的实现。
二、简介
刚刚咱们也介绍了,在 Map 家族中,WeakHashMap 是一个很特殊的成员,它的特殊之处在于 WeakHashMap 里的元素可能会被 GC 自动删除,即使程序员没有显示调用 remove() 或者 clear() 方法。
换言之,当向 WeakHashMap 中添加元素的时候,再次遍历获取元素,可能发现它已经不见了,我们来看看下面这个例子。
public static void main(String[] args) { Map weakHashMap = new WeakHashMap(); //向weakHashMap中添加4个元素 for (int i = 0; i < 3; i++) { weakHashMap.put("key-"+i, "value-"+ i); } //输出添加的元素 System.out.println("数组长度:"+weakHashMap.size() + ",输出结果:" + weakHashMap); //主动触发一次GC System.gc(); //再输出添加的元素 System.out.println("数组长度:"+weakHashMap.size() + ",输出结果:" + weakHashMap); }
输出结果:
数组长度:3,输出结果:{key-2=value-2, key-1=value-1, key-0=value-0} 数组长度:3,输出结果:{}
当主动调用 GC 回收器的时候,再次查询 WeakHashMap 里面的数据的时候,内容为空。
更直观的说,当使用 WeakHashMap 时,即使没有显式的添加或删除任何元素,也可能发生如下情况:
- 调用两次 size() 方法返回不同的值;
- 两次调用 isEmpty() 方法,第一次返回 false,第二次返回 true;
- 两次调用 containsKey() 方法,第一次返回 true,第二次返回 false,尽管两次使用的是同一个key;
- 两次调用 get() 方法,第一次返回一个 value,第二次返回 null,尽管两次使用的是同一个对象。
要明白 WeekHashMap 的工作原理,还需要引入一个概念:弱引用。
我们都知道 Java 中内存是通过 GC 自动管理的,GC 会在程序运行过程中自动判断哪些对象是可以被回收的,并在合适的时机进行内存释放。
GC 判断某个对象是否可被回收的依据是,是否有有效的引用指向该对象。如果没有有效引用指向该对象(基本意味着不存在访问该对象的方式),那么该对象就是可回收的。
2.1、对象引用介绍
从 JDK1.2 版本开始,把对象的引用分为四种级别,从而使程序更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
用表格整理之后,各个引用类型的区别如下:
2.1.1、强引用
强引用是使用最普遍的引用,例如,我们创建一个对象:
//强引用类型 Object object=new Object();
如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足, Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
如果不使用时,要手动通过如下方式来弱化引用,如下:
//将对象设置为null,帮助垃圾收集器回收此对象 object=null;
这个时候,GC 认为该对象不存在引用,就可以回收这个对象,具体什么时候收集这要取决于 GC 的算法。
2.1.2、软引用
被SoftReference
指向的对象,属于软引用,如下: