强引用
Object object1 = new Object();
平时我们写的所有带等号将引用指向对象的引用方式都是强引用
它的特点是如果一个对象有根节点的强引用,那么 GC 绝对不会回收它;如果这个对象没有被引用指向,那么它就会被回收
软引用
SoftReference<Object> object2 = new SoftReference<>(new Object());
需要使用 SoftReference(reference 查询)来让一个引用软指向一个对象
它的特点是一个对象有软引用时,当内存不够时,GC 会回收这个对象
它常常用来做缓存,存放一些数据量较大的数据,比如图片,富文本之类的
弱引用
WeakReference<Object> Object3 = new WeakReference<>(new Object());
使用 WeakReference 来让一个引用弱指向一个对象
GC 会回收只被弱引用指向的对象,不管内存够不够,注意,不管是软引用还是弱引用,被回收的都是对象,引用是不被回收的。被弱引用关联的对象只能生存到下一次垃圾收集发生为止
ThreadLocal 中的键值对,就是一个弱引用,准确的说是继承了一个弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
弱引用代码
public class WeakReference<T> extends Reference<T> {
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
父类 Reference 的部分代码
public abstract class Reference<T> {
...
private T referent;
volatile ReferenceQueue<? super T> queue;
public T get() {
return this.referent;
}
public void clear() {
this.referent = null;
}
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
}
虚引用
private ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> object4 = new PhantomReference<>(new Object(), queue);
使用 PhantomReference 来让一个引用虚指向一个对象,我们无法通过一个虚引用来取得一个对象实例,它一般需要配合一个队列来使用
它的唯一作用就是为了能在这个对象被回收的时候可以收到一个系统通知,一般用来管理堆外内存
JVM 可以控制一些不再堆中的内存,这些内存是操作系统的,使用堆外内存的好处是当数据从网上下载时不用操作系统拷贝数据给 JVM。NIO 以及 Netty 的 zero copy 都使用了这种技术
JVM 使用一个对象来代表这些堆外内存,这个对象叫 DirectByteBuffer。当 DirectByteBuffer 这个对象被回收的时候我们应该回收对应的堆外内存,不然会造成 OOM
我们使用一个虚引用指向 DirectByteBuffer,当 DirectByteBuffer 被回收时,说明我们不需要这个堆外内存中的数据了,虚引用会将对应信息放入引用队列中(幻像引用的构造函数必须指定引用队列,而其他引用类型没有引用队列一样可以运行,强引用无法放进引用队列。引用队列主要用于存放软引用、弱引用和虚引用对象),GC 线程监视着这个队列,如果有东西进来了,它会调用函数清理对应的堆外内存(这里的函数是我们定义的,相当于虚引用提供了一个回调功能)
终结器引用
四大天王有五个是常识。终结器引用用来执行对象的 finalize 方法
没有强引用引用对象时,在对象被 GC 时,虚拟机创建终结器引用,垃圾回收时将终结器引用加入引用队列,一个优先级很低的线程 Finalizer 查看引用队列中是否有终结器引用,通过终结器引用找到这个对象调用对象的 finallize 方法,然后进行垃圾回收
因此,终结器一般用于对象的回收跟踪
finallize 方法只能执行一次,在对象的 finalize 方法被调用后,对象会被标记为 finaized,并且不会再次进入 finalize 方法。finallize 可以用于自救,将某个引用指向这个对象,不过不推荐使用,就算这么做了,在第二次 gc 的时候,该对象还是会被回收