在 JDK 中,引用和对象一样,已经被抽象成了类,如下图
强引用 FinalReference
强引用 Finalizer
Finalizer 是对 FinalReference 的实现,也是非 public 的
实现有 protected void finalize() throws Throwable { }
方法的对象会被识别为 Finalizer 对象
Finalizer 对象由 JVM 自动调用 register() 方法,完成创建,并在创建时将自己关联引用队列
Finalizer 通过 finalize()
定义了对象销毁时的自定义动作
Finalizer 对象至少需要两轮 GC 才能被回收,这是为了使被忘记释放资源的对象,即使真被忘了,也不会肯定不被回收
finalize()
将 Finalizer 对象的连接放到它的引用队列中,但因为 Finalizer 本身就是个强引用所以还没有被 GC软引用 SoftReference
Reference ra = new SoftReference<>(new AAA());
Map> cache = new HashMap<>();
public class SoftReferenceDemo {
public AAA b = new AAA();
private Reference<AAA> ra = new SoftReference<>(new AAA());
public Reference<AAA> getRa() {
return ra;
}
public static void main(String[] args) {
SoftReferenceDemo demo = new SoftReferenceDemo();
System.out.println("================================");
System.out.println(demo.b);
System.out.println(demo.getRa().get());
System.out.println("================================");
try {
int[] boom = new int[30*1024*1024];
}catch (Throwable e){
System.out.println("********************************");
System.out.println("boom !!!");
System.out.println("********************************");
}finally {
System.out.println("================================");
System.out.println(demo.b);
System.out.println(demo.getRa().get());
System.out.println("================================");
}
}
}
弱引用 WeakReference
Reference ra = new WeakReference<>(new AAA());
Map map = new HashMap<>();
或 WeakHashMap
public class WeakReferenceDemo {
public AAA b = new AAA();
private Reference<AAA> ra = new WeakReference<>(new AAA());
public Reference<AAA> getRa() {
return ra;
}
public static void main(String[] args) {
WeakReferenceDemo demo = new WeakReferenceDemo();
System.out.println("================================");
System.out.println(demo.b);
System.out.println(demo.getRa().get());
System.out.println("================================");
System.gc();
System.out.println("================================");
System.out.println(demo.b);
System.out.println(demo.getRa().get());
System.out.println("================================");
}
}
WeakHashMap
WeakHashMap 是个 弱引用 版的 HashMap,其内部的 WeakHashMap.Entry 中 key 的类型为 WeakReference
// 此 Entry 非彼 Entry
Entry<K,V>[] table;
public WeakHashMap(int initialCapacity, float loadFactor) {
// 省略
table = newTable(capacity);
// 省略
}
// 此 Entry 中的 key 是 弱引用,所以一旦 GC,只要 key 不被其他强引用指向, WeakHashMap 就地清空
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
// 构造,注意 key 才是弱引用
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;
}
//省略
}
简单的测试
虚引用 PhantomReference
ReferenceQueue + WeakReference
默认的,如果 SoftReference、WeakReference、PhantomReference 的对象 在声明时指定了 引用队列,如下例,则被 GC 时会执行 finalize() 方法,对象销毁后,上述引用本身不在指向对象,但 对象会存入 引用队列
public void test() throws InterruptedException {
ReferenceQueue<AAA> rf = new ReferenceQueue<>();
Reference<AAA> weak = new WeakReference<>(new AAA(),rf);
System.out.println(weak.get());//base.learning.AAA@3cd1a2f1
System.out.println(rf.poll());//null
System.gc();
TimeUnit.SECONDS.sleep(1);
System.out.println(weak.get());//null
System.out.println(rf.poll());//引用还在 java.lang.ref.WeakReference@2f0e140b
// System.out.println(rf.poll().get());//引用不指向对象了 jnull
}
ReferenceQueue + PhantomReference
public void test() throws InterruptedException {
ReferenceQueue<AAA> rf = new ReferenceQueue<>();
Reference<AAA> phantom = new PhantomReference<>(new AAA(),rf);
System.out.println(phantom.get());
System.out.println(rf.poll());
System.gc();
TimeUnit.SECONDS.sleep(1);
System.out.println(phantom.get());
System.out.println(rf.poll());
}
四大引用对比
四大引用的区别,主要集中在 对 GC 抵抗能力 上
强 | 软 | 弱 | 虚 | |
---|---|---|---|---|
对应类 | Reference | SoftReference | WeakReference | PhantomReference |
是否常见 | √ | × | × | × |
使用方式 | 常规场景都是 比如正常赋值 但不能如其他引用直接声明 | Reference | 类似 SoftReference | 配合引用队列 new PhantomReference<>(new AAA(),rf); |
只持有此类引用的对象遇到垃圾回收时 | OOM 了都不收 | 内存不足时回收 | 只要 GC 就回收 | 随时回收 |
应用场景 | 遍地都是 | 内存敏感缓存(如 mybatis 中) | 解决容器不移除过期 key 导致的内存泄漏 WeakHashMap | 扩展 finalization 机制 |
ReferenceQueue
与 finalize()
的区别
finalize()
是 JVM 回收对象之前自动调用,即在回收之前生效ReferenceQueue
可以在对象被回收之后,额外保存其引用,即在回收之后生效