一:Reference与ReferenceQueue
Reference的四种状态:
- Active:活跃,内存一开始分配的常有状态,垃圾收集器进行对该引用可达性分析后会进入Pending或Inactive状态,这取决于引用创建的时候是否绑定到一个ReferenceQueue。
- Pending:待定,进行回收时,会先进入pending-Reference队列,等待着Reference-handler线程来取走入队。
- Enqueued:入队,内存已回收的对象,若从队列中删除,则转为Inactive状态。
- Inactive:不活跃,最终状态,不可改变。
回收过程引用状态变化:
ReferenceQueue queue = new ReferenceQueue();
//一开始创建的时候,reference状态为Avtice
WeakReference reference = new WeakReference<>(new Object(),queue);
//要进行回收了,进入Pending状态,在Reference-handler队列中等待ReferenceHandler线程过来取走
//ReferenceHandler线程是垃圾回收器的线程
//取走引用,加入ReferenceQueue中,状态改为Enqueued
System.gc();
try {
//从队列中取走引用,状态改为Inactive
Reference remove = queue.remove();
}catch (Exception e){
e.printStackTrace();
}
ReferenceQueue:
它就是一个引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到Pending队列中,ReferenceQueue实现对引用的入队(enqueue)和出队(poll),remove操作。
Reference构造方法:
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
//ReferenceQueue.NULL:继承ReferenceQueue
//重写enqueue方法,直接返回false,起到状态表示作用
private static class Null<S> extends ReferenceQueue<S> {
boolean enqueue(Reference<? extends S> r) {
return false;
}
}
Reference成员变量:
-
private T referent;
由构造器赋值,指的是传入java对象的引用 -
private static Reference pending = null;
Pending队列,由JVM的垃圾回收器赋值,当对象除了被reference引用之外没有其它强引用了,jvm的垃圾回收器就会将指向需要回收的对象的Reference都放入到这个队列里面,Reference指的是Reference的成员变量。内存回收时,先加入该队列,等待Reference-handler线程来进行remove,加入ReferenceQueue中,并使用discovered字段来链接它的数据。 -
transient private Reference discovered
表示要处理的对象的下一个对象,由垃圾回收器进行赋值。 -
volatile ReferenceQueue<?super T> queue;
ReferenceQueue队列,但它只持有这个链表的表头对象header,这个链表是由Refence对象里面的next成员变量构建起来的,next也就是链表当前节点的下一个节点,所以Reference对象本身就是一个链表的节点。 -
Reference next;
用来与queue成员变量一同组成ReferenceQueue队列 -
static private class Lock { };
-
private static Lock lock =new Lock();
lock成员变量是pending队列的全局锁,ReferenceHander线程run方法里面用到pending队列。
静态代码块:
static {
//获得当前线程的所有线程
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
//设置最大优先级,守护线程
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
public boolean tryHandlePendingReference() {
return tryHandlePending(false);
}
});
}
当 Refrence 类被加载的时候,会创建启动 ReferenceHandler 线程,设为守护线程并设置线程的级别为最大级别。
现在看一个ReferenceHandler 线程的run方法
private static class ReferenceHandler extends Thread {
//...
public void run() {
while (true) {
tryHandlePending(true);
}
}
}
static boolean tryHandlePending(boolean waitForNotify) {
Reference<Object> r;
Cleaner c;
try {
synchronized (lock) {
//检查pengding是否为空
if (pending != null) {
r = pending;
c = r instanceof Cleaner ? (Cleaner) r : null;
pending = r.discovered;
r.discovered = null;
} else {
if (waitForNotify) {
lock.wait();
}
return waitForNotify;
}
}
} catch (OutOfMemoryError x) {
Thread.yield();
return true;
} catch (InterruptedException x) {
return true;
}
if (c != null) {
c.clean();
return true;
}
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
在 tryHandlePending 方法里面,检查 pending 是否为 null,如果pending不为 null,则将 pending 进行 enqueue,否则线程进入 wait 状态,discovered和 pending 是由垃圾回收器进行赋值的。
boolean enqueue(Reference<? extends T> r) {
synchronized (lock) {
ReferenceQueue<?> queue = r.queue;
// queue 为 null 或者 queue 已经被回收了,直接返回
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this;
//设置状态
r.queue = ENQUEUED;
// 将 refrence 进行前插入
r.next = (head == null) ? r : head;
head = r;
queueLength++;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(1);
}
lock.notifyAll();
return true;
}
}
二:Refrence 的子类
- StrongReference
java没有这个类,但强引用比较普遍,若果一个对象是强引用,那回收器则始终都不会回收该对象。 - SoftReference
如果一个对象只有软引用,则在内存充足的情况下是不会回收此对象的,但是,在内部不足即将要抛出OOM异常时就会回收此对象来解决内存不足的问题。 - WeakReference
只要 GC 发现一个对象只有弱引用,则就会回收此弱引用对象。但是由于GC所在的线程优先级比较低,不会立即发现所有弱引用对象并进行回收。只要GC对它所管辖的内存区域进行扫描时发现了弱引用对象就进行回收。 - PhantomReference
PhantomReference,即虚引用,虚引用并不会影响对象的生命周期。虚引用的作用为:跟踪垃圾回收器收集对象这一活动的情况。
当GC一旦发现了虚引用对象,则会将PhantomReference对象插入ReferenceQueue队列,而此时PhantomReference对象并没有被垃圾回收器回收,而是要等到ReferenceQueue被你真正的处理后才会被回收。
注意:PhantomReference必须要和ReferenceQueue联合使用。