4种引用对比
强引用: 只要强引用还在就不会被 GC,JVM 宁愿抛出 OutOfMemoryError 错误也不会回收;
软引用(SoftReference): 用来描述非必需对象, GC 时发现内存不够时(Heap 内存超阈值)将会被回收。当垃圾回收器决定对其回收时,会先清空它的 SoftReference,也就是说 SoftReference 的
get()
方法将会返回 null,然后再调用对象的finalize()
方法,并在下一轮 GC 中对其真正进行回收。弱引用(WeakReference): 也是用来描述非需对象的, 无论内存够不够,下次 GC 时一定都被回收, 但对象同时也被强引用持有,则不会回收;
虚引用(PhantomReference): PhantomReference 无法用来获取对象,其 get 方法永远返回 null,为一个对象设置虚引用关联的唯一目的是跟踪对象被垃圾回收的状态,通过查看引用队列中是否包含对象所对应的虚引用来判断它是否即将被垃圾回收,当
PhantomReference
被放入队列时,说明 referent 的finalize()
方法已经调用,并且垃圾收集器准备回收它的内存了。FinalReference 以及 Finzlizer:@todo
相较于传统的引用计数算法,Java 使用可达性分析来判断一个对象是否存活。其基本思路是从 GC Root 开始向下搜索,如果对象与 GC Root 之间存在引用链,则对象是可达的。对象的可达性与引用类型密切相关。Java 有5中类型的可达性:
强可达(Strongly Reachable):如果线程能通过强引用访问到对象,那么这个对象就是强可达的。
软可达(Soft Reachable):如果一个对象不是强可达的,但是可以通过软引用访问到,那么这个对象就是软可达的
弱可达(Weak Reachable):如果一个对象不是强可达或者软可达的,但是可以通过弱引用访问到,那么这个对象就是弱可达的。
虚可达(Phantom Reachable):如果一个对象不是强可达,软可达或者弱可达,并且这个对象已经finalize过了,并且有虚引用指向该对象,那么这个对象就是虚可达的。
不可达(Unreachable):如果对象不能通过上述的几种方式访问到,则对象是不可达的,可以被回收。
How to Use:
ReferenceQueue<String> queue = new ReferenceQueue<>(); // 对象被回收后, 被放入q里 |
从 Soft、Weak、Phantom 三种引用的共同特点来看,都可以设置一个 ReferenceQueue,在 ref 关联的对象被回收时,ref 被放入 ReferenceQueue 中;
那么这个机制是如何实现的呢?
ReferenceQueue 的工作机制
class Reference { |
- Reference 其引用的对象被回收后,垃圾回收器将其加入到 Reference.pending 链表(所有的 Reference 共享一个链表)
- Reference 类还包含一个 ReferenceHandler 线程(在 Reference 类的 static 代码中创建,同样是所有的 Reference 共享一个),此线程的从 pending-Reference 取出引用对象,将其加入它的 ReferenceQueue
- 用户的代码里读取 ReferenceQueue,自行处理
/* High-priority thread to enqueue pending References |
WeakHashMap
WeakHashMap 的特点:
- WeakHashMap 会“被动的”清理放入 Map 的 Entry,适用于程序内缓存的场景;
- WeakHashMap.Entry ,出了继承
Map.Entry<K,V>
,还继承了 WeakReference ;
How to Use:
protected static void weakHashMapTest() { |
第一次 gc 后,两个 Entry 都可以获取到,第二次 gc 后,遍历 WeakHashMap 只有 K2的 Entry 了;
WeakHashMap 自动回收的特性可以作为缓存来用, 例如 tomcat 的 ConcurrentCache。
WeakHashMap 源码解析
WeakHashMap 的主要属性:
public class WeakHashMap<K,V> |
put(k,val):过程与 HashMap 类似,但需要注意的是 WeakHashMap.Entry:
- WeakHashMap 的桶数组定义是:
Entry<K,V>[] table
- WeakHashMap.Entry 继承自 WeakReference;
- WeakHashMap.Entry 的成员只有 value,没有 key(也就意味着 Entry 对象不会对 key 有强引用);
- put 方法传入的 key,被转换为了一个弱引用,同时该弱引用关联 WeakHashMap.queue :
Entry(Object key, V value, |
当把一个 Entry put 后,再分析一下对象的可达性:
- 如果 weakHashMap 是强可达的,那么 entry 对象也是强可达;
- value 对象被 entry.value 引用,强可达;
- key 对象只有一个弱引用关联(弱可达),所以会被 GC 掉,同时 GC 线程把 Key 关联的弱引用,放入 WeakHashMap 对象的 queue;
那么WeakHashMap 是如何回收 value 的?
在 get()
put()
, size()
方法里都会先调用一个 expungeStaleEntries()
方法,
此方法遍历 ReferenceQueue 取出每个弱引用(因为 Entry 继承自 WeakReference),把 Entry.val 设置为 null 帮助回收:
private void expungeStaleEntries() { |
代码(2)处把 Entry.value 设置为 null 帮助回收 @doubt: Entry<K,V> e = (Entry<K,V>) x
,x 是从 queue 里取出的,也即 Reference
@ref: