自从jdk 1.2 开始, Java的引用类型分就为四种, 强引用Strong reference, 软引用Soft reference, 弱引用weak reference,虚引用 phantom reference。Reference 是一个抽象类,而 SoftReference,WeakReference,PhantomReference 以及 FinalReference 都是继承它的具体类。下面分析一下四种类型的特点及GC 是如何垃圾回收的
1.StrongReference
位于java.lang.ref 包下,这是使用最多的类型, 比如new 一个对象obj, 当我们在Heap中创建了对象obj, 这就是一个强引用类型对象。对于强引用对象, 垃圾回收器在进行垃圾回收时如果发现该对象没有被任何引用时才会回收,否则会一直在Heap中,对于jvm OOM(out of memory error), 一般都是因为太多的强引用类型的对象没有被回收导致内存溢出。
Object obj = new Object();
特点:
强引用可以直接访问目标对象。
强引用所指向的对象在任何时候都不会被系统回收。
强引用可能导致内存泄漏。
2.SoftReference
当JVM内存不够时,GC 才会回收只有SoftReference 引用的对象, 一般可用作缓存
Object obj = new Object();
SoftReference<Object> softReference = new SoftReference<Object>(obj);
或者
Object obj = new Object();
SoftReference<Object> softReference = new SoftReference<Object>(obj);
特点:
软引用使用 get() 方法取得对象的强引用从而访问目标对象。
软引用所指向的对象按照 JVM 的使用情况(Heap 内存是否临近阈值)来决定是否回收。
软引用可以避免 Heap 内存不足所导致的异常。
3.WeakReference
不管JVM 内存是否满, GC 都会回收只有WeakReference引用的对象
Object obj = new Object();
WeakReference<Object> weakReference = new WeakReference <Object>(obj);
或者
ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
WeakReference <Object> weakReference = new WeakReference <Object>(obj, rq);
ReferenceQueue:主要是用来监听GC 对 reference对象的回收, 看下面的代码
private static class ReferenceHandler extends Thread {
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
for (;;) {
Reference r;
synchronized (lock) {
if (pending != null) {
r = pending;
Reference rn = r.next;
pending = (rn == r) ? null : rn;
r.next = r;
} else {
try {
lock.wait();
} catch (InterruptedException x) { }
continue;
}
}
// Fast path for cleaners
if (r instanceof Cleaner) {
((Cleaner)r).clean();
continue;
}
ReferenceQueue q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
}
}
}
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
}
pending是由jvm来赋值的,当Reference内部的referent对象的可达状态改变时,jvm会将Reference对象放入pending链表。一旦relevent= null,垃圾收集时会标记WeakReference的referent为不可达get()返回null,并且通过 赋值给pending ,触发ReferenceHandler线程处理pending。ReferenceHandler线程要做的是将pending对象enqueue,但默认我们所提供的queue,也就是从构造函数传入的是null,实际是使用了ReferenceQueue.NULL,Handler线程判断queue为ReferenceQueue.NULL则不进行操作,只有非ReferenceQueue.NULL 的queue才会将Reference进行enqueue。
当创建一个SoftReference/WeakReference对象后,会自动启动一个Demon thread, 该线程主要负责清理reference对象(对应于 private T referent;), 并将SoftReference/WeakReference对象放入ReferenceQueue。测试代码如下
public class ReferenceQueueTest {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
WeakReference<Object> softReference = new WeakReference<Object>(obj, rq);
System.out.println(softReference.get());
System.out.println(rq.poll());
obj=null;
System.gc();
Thread.sleep(1000);
System.out.println(softReference.get());
System.out.println(rq.poll());
}
}
结果:
java.lang.Object@de6ced
null
null
java.lang.ref.WeakReference@14318bb
特点:
弱引用使用 get() 方法取得对象的强引用从而访问目标对象。
一旦系统内存回收,无论内存是否紧张,弱引用指向的对象都会被回收。
弱引用也可以避免 Heap 内存不足所导致的异常。
4.PhantomReference
虚引用是最弱的引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也不能通过get()方法来获得对象的引用,虚引用主要用来跟踪对象的垃圾回收
public class PhantomReference<T> extends Reference<T> {
public T get() {
return null;
}
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
特点
虚引用永远无法使用 get() 方法取得对象的强引用从而访问目标对象。
虚引用所指向的对象在被系统内存回收前,虚引用自身会被放入 ReferenceQueue 对象中从而跟踪对象垃圾回收。
虚引用不会根据内存情况自动回收目标对象。