WeakHashMap
小高飞 人气:0WeakHashMap
今天在阅读《Effective Java》的【消除过期的对象引用】一章中,讲到了WeakHashMap,发现对该类不怎么熟悉,因此记录下整理的相关知识。
Java的引用类型
在学习WeakHashMap之前,得知道Java的4个引用类型:
-
强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。比如String str = "hello"这时候str就是一个强引用。
-
软引用:内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。
-
弱引用:如果一个对象具有弱引用,在垃圾回收时候,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。
-
虚引用:如果一个对象具有虚引用,就相当于没有引用,在任何时候都有可能被回收。使用虚引用的目的就是为了得知对象被GC的时机,所以可以利用虚引用来进行销毁前的一些操作,比如说资源释放等。
而WeakHashMap的键就是弱引用,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。
WeakReference
在WeakHashMap中真正实现弱引用的是WeakReference类,该类有两个构造方法:
public class WeakReference<T> extends Reference<T> { public WeakReference(T referent) { super(referent); } public WeakReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); } }
-
WeakReference(T referent):referent就是被弱引用的对象(注意区分弱引用对象和被弱引用的对应,弱引用对象是指WeakReference的实例或者其子类的实例),比如有一个Apple实例apple,可以如下使用,并且通过get()方法来获取apple引用。也可以再创建一个继承WeakReference的类来对Apple进行弱引用,下面就会使用这种方式。
-
WeakReference(T referent, ReferenceQueue<? super T> q):与上面的构造方法比较,多了个ReferenceQueue,在对象被回收后,会把弱引用对象,也就是WeakReference对象或者其子类的对象,放入队列ReferenceQueue中,注意不是被弱引用的对象,被弱引用的对象已经被回收了。
WeakHashMap
WeakHashMap的Entry继承了WeakReference,而后在构造方法中调用父类WeakReference的构造方法并将key设为被弱引用对象。
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; final int hash; Entry<K,V> next; 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内部有一个expungeStaleEntries函数,在这个函数内部实现移除其内部不用的entry从而达到的自动释放内存的目的。因此我们每次访问WeakHashMap的时候,都会调用这个expungeStaleEntries函数清理一遍。因为涉及到链表,所以下面代码讲解中将Entry称为链表中的一个节点。
private void expungeStaleEntries() { //如果引用队列不为空,则说明有被弱引用的对象被GC回收,则需释放被弱引用的对象的Entry for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) x; //获取该entry在Entry集合table中的索引位置 int i = indexFor(e.hash, table.length); //从table[i]节点处开始往后截取一段链表 Entry<K,V> prev = table[i]; //创建p指向当前节点,初始和前一个节点prev是同一个节点 Entry<K,V> p = prev; while (p != null) { //创建next节点指向当前节点p的下一个节点 Entry<K,V> next = p.next; //如果当前节点等于被弱引用的对象的节点e,则需让当前节点失去引用,让e节点进入到下一波GC回收中 if (p == e) { //如果前一个节点也等于被弱引用的对象的节点e,则说明当前节点p和前一个节点prev是同一个节点,即需被弱引用的对象的节点e是截取链表段的头节点,则将table[i]设为下一节点,节点e失去引用 if (prev == e) table[i] = next; //反之,则说明被弱引用的对象的节点e是前一个节点prev的下一个节点,则将prev的下一个节点设为节点e的下一个节点,节点e失去引用 else prev.next = next; e.value = null; //将e的值设为null,加速垃圾回收 size--; break; } prev = p; p = next; } } } }
加载全部内容