Java中的引用类型:
强引用
软引用:缓存
public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}
//ReferenceQueue可传可不传
public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}
弱引用:伴生(Scala)引用数据
public WeakReference(T referent) {
super(referent);
}
//ReferenceQueue可传可不传
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
虚引用:对象跟踪,不是用来存数据的,调用get()永远返回null,
//ReferenceQueue必传
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
Finalize引用(FinalReference):用户不可见,重写finalize方法自动生成,JVM用的
public abstract class Reference<T> {
private static class ReferenceHandler extends Thread {
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
while (true) {
tryHandlePending(true);
}
}
static boolean tryHandlePending(boolean waitForNotify) {
Reference<Object> r;
Cleaner c;
try {
synchronized (lock) {
if (pending != null) {
r = pending;
//当 DirectByteBuffer被 gc时,jvm垃圾回收线程会将 Cleaner引用加入到
//pendding队列中去,比如DirectByteBuffer,这里会赋值给c
c = r instanceof Cleaner ? (Cleaner) r : null;
// unlink 'r' from 'pending' chain
pending = r.discovered;
r.discovered = null;
} else {
if (waitForNotify) {
lock.wait();
}
// retry if waited
return waitForNotify;
}
}
} catch (OutOfMemoryError x) {
Thread.yield();
// retry
return true;
} catch (InterruptedException x) {
// retry
return true;
}
//这里就会执行DirectByteBuffer中的clean方法
// Fast path for cleaners
if (c != null) {
c.clean();
return true;
}
//如果是Cleaner上一步就直接返回了,如果不是,那就会放到ReferenceQueue中。这个
//ReferenceQueue就是我们实例化时传进来的
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
}
//链表的头部,是static的
private static Reference<Object> pending = null;
//链表的next
transient private Reference<T> discovered; /* used by VM */
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);
}
});
}
}
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
synchronized (lock) {
ReferenceQueue<?> queue = r.queue;
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this;
//使用头插法放入队列
r.queue = ENQUEUED;
r.next = (head == null) ? r : head;
head = r;
queueLength++;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(1);
}
lock.notifyAll();
return true;
}
}
pending是一个静态的,因为ReferenceHandler要处理所有引用,那么所有引用都必须得挂到这个链表上,那么如何让所有引用对象共享同一个链表呢?所以就设置成static的。
为什么discovered是实例类型不是静态类型呢?
因为我的对象是不同的,每种引用类型都有一个实例变量,所以每个引用类型都有一个Reference,discovered是指向下一个实例变量的,所以也是实例变量
sun.misc.VM.addFinalRefCount(1);这一步是看对象有没有重写过finalize(),如果重写过,那就引用计数+1,所以真正回不回收还要看执行完finalize(),有可能起死回生。
JVM是怎么把引用对象挂到pending列表的呢?
NIO使用一个虚引用解决堆外内存回收问题,那Netty怎么解决呢?
道理一样,也是给ByteBuf添加一个虚引用,当引用被干掉时去检查ByteBuf引用的那块内存是否回收