新手读源码__java中的4种引用+WeakHashMap的弱引用的底层实现
前言
在《深入JVM》中提到过四种引用,但是对它们的认识却很少。如今又遇到了WeakHashMap,里面是弱引用,所以回过头来把4种引用的坑填上。通过本篇文章你可以了解:
- 4种引用
- 引用的几种状态
- 如何实现弱引用的回收
- WeakHashMap中弱引用回收机制
笔者源码来自
- JAVA9
4种引用
引用 | 介绍 |
---|---|
强引用 | 不会被GC的引用 |
弱引用(WeakReference) | 弱引用在下一次GC时会被收集 |
软引用(SoftReference) | 软引用在内存满的时候会被GC,也就是OOM的时候 |
虚引用(OhantomReference) | 虚引用不会被get返回,GC不会自动清理虚引用,虚引用不会影响一个存在虚引用对象的GC回收 |
开场白,引用的四种状态
—————————–翻译源码的四种状态解释———————-
Active:新创建的实例对象处于active状态,当GC显示已经到了合适的状态之后,会使得实例的状态改变为Pending 状态,把实例对象加入Pending队列或者Inactive状态
Pending:pending- Reference list当中的一个元素,等待被Reference-handler 线程入队处理,没有注册过的实例不会进入这个状态
Enqueued: 队列中的一个元素,这个实例在被创建的时候被注册了。当实例从它的ReferenceQueue中移除的时候,会被设置成Inactive。没有被注册的实例不会到达这个状态
Inactive:最终状态不会再被改变
————————————四种状态源码的实现—————————-
- Active 的时候,ReferenceQueue实例注册了,就是初始化的时候指定了一个引用队列。或者没有注册queue的时候ReferenceQueue.NULL。next是null。可以这么认为
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
Pending 状态,queue必须注册,next=this
Enqueued状态,queue必须注册,next为队列中的下一个引用,如果是该队列中的最后一个就是this
Inactive状态,queue为ReferenceQueue.NULL
主要的流程
其主要的流程图如下。
或者可以这么将,上面的那条路线是程序员手动处理队列的方法,下面是交给GC智能处理的方法。这样理解就很容易了
WeakHashMap
WeakHashMap和HashMap差不多,主要的区别就在于清除引用队列的函数,让我们挑几点重要的去看看它
构造函数
构造函数我们只看重要的部分,节点部分
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;
}
这段的重点在于继承了WeakReference,并且将键作为弱引用,以及队列作为参数调用WeakReference类的构造方法。
public WeakReference(T referent, ReferenceQueue<? super T> q) {
# 弱引用使用的是Reference的构造方法
super(referent, q);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
从以上几段代码,WeakHashMap是使用队列来实现的!
WeakHashMap的清除过程
在WeakHashMap中,很多方法都包含下面的一个清除函数,用来清除队列中的弱引用
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;
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;
}
}
}
}
重点:
由此可以看出WeakHashMap清除弱引用的流程,采用的是Reference图上面的方法,用一个队列来保存弱引用,然后从WeakHashMap中GC掉存在于ReferenceQueue中的弱引用,同时清除ReferenceQueue中存放的元素。所有上面的铺垫其实都是为了这段的总结。
实例讲解
import java.util.HashMap;
import java.util.WeakHashMap;
public class Test {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
WeakHashMap<String, Integer> weakmap = new WeakHashMap<>();
//String a = "01";
//String b = "02";
String a = new String("11");
String b = new String("22");
map.put(a, 1);
map.put(b, 2);
weakmap.put(a, 1);
weakmap.put(b, 2);
map.remove(a);
a = null;
b = null;
System.gc();
map.forEach((k, v) -> {
System.out.println("HashMap: key: "+k+"Value: "+v);
});
weakmap.forEach((k, v) -> {
System.out.println("WeakMap: key: "+k+"Value: "+v);
});
}
}
----结果----
HashMap: key: 22Value: 2
WeakMap: key: 22Value: 2
这个例子比较经典,首先a,b都变为null。解除了引用,map手动移除了a,所以除了WeakMap中存在a的引用,别处都不存在了,所以在访问的时候自动清楚了a的引用,weakMap中只存在b的引用。
总结
我们首先从源头Reference类,观察了引用类的生命周期几种状态和GC方法,一种是自动GC一种是使用ReferenceQueue的回收,WeakHashMap采用的是下面一种,队列回收法。关于细节部分未尽详细,但是搞清楚了大概的流程,以及WeakHashMap的运作。全局把握即可,如上都是笔者自己的理解,可能有些地方不够正确,望纠正。
上一篇: 递归整理