强引用、软引用(java.lang.ref.SoftReference)、弱引用(java.lang.ref.WeakReference)、虚引用(java.lang.ref.PhantomReference)。
java默认的引用类型为强引用,比如 Object a = new Object();其中 a 为强引用,new Object()为一个具体的对象。
至于软应用,弱引用,虚引用,就是 JAVA 虚拟机管理对象的范畴了,可以这样理解,SoftReference、WeakReference、PlantomReference 只是一种标记,JAVA 虚拟机在垃圾回收时,对上述不同的标记【引用的对象】采取不同的措施。采取措施如下:
软引用(SoftReference):当内存足够时,该引用【引用的对象】不会被回收,那什么是内存足够呢?进行年轻代的垃圾回收不会触发SoftReference所指向对象的回收,如果触发Full GC,那SoftReference所指向的对象将被回收。
弱引用(WeakReference) :当进行年轻代垃圾回收时,该引用指向的对象,就会被回收。
虚引用(PhantomeReference) 该引用指向的对象,无法对垃圾收集器收集对象时产生任何影响,唯一有用的是,如果被垃圾收集器收集的对象,被PhantomeReference标记,垃圾收集器会通过注册在PhantomeReference上的队列来通知应用程序,该引用指向的对象,已经被垃圾收集器回收。
从上文的描述,也清楚的知道,上述应用是直接JVM打交道,更直接的说是与垃圾回收器直接的交互。
boolean enqueue(Reference extends T> r) { /* Called only by Reference class */
synchronized (r) {
if (r.queue == ENQUEUED) return false; //关注这里
synchronized (lock) {
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;
}
}
}
JAVA 四种引用的理解就到这了,其实 JAVA 中还有一种引用,java.lang.ref.FinalReference 应用,不过修饰符是 default, 包访问权限,主要用于 finalizer方法的执行,请关注下一篇博文。
再统一聊聊 java 引用中涉及到的引用的几个队列。
Reference中涉及到的队列(链表)
Reference next;
private static Reference pending = null;
private ReferenceQueue queue;
每个引用可以关联一个引用队列,该引用队列由应用程序创建的,,然后垃圾回收器在检测到引用不可达时,将该引用加入到该队列,应用程序可以根据该引用队列来做些处理。(也就是该引用队列 成为 垃圾回收器与应用程序的通信机制)。
ReferenceQueue 自身的结构
private volatile Reference extends T> head = null;
首先,应用程序如下使用引用:
public class TestReference {
private static ReferenceQueue aQueue = new ReferenceQueue();
public static void main(String args) {
Object a = new Object(); // 代码1
WeakReference ref = new WeakReference( a, aQueue );
}
}
然后在程序运行过程,内存不断消耗,直至触发垃圾回收操作。此时,垃圾收集器发现 代码1处的 a 所指向的对象,只有 ref引用它,从根路径不可达,故垃圾回收器,会将 ref 引用加入到 static Reference pending 链表中。【注意,此代码是写在JVM实现中的】
所处理的操作无非就是【 1、如果pending 为空,则将当前引用(ref) 设置为pengding,,并且将 ref对象的next指针指向自己; 如果pending不为空,则将当前的引用(ref)的next指向pengding,然后pengding = 当前的引用ref 】,所以 pengding 其实就是 一个后进新出的链表单向链表结构。
从pengding 链表中取出引用,进行入队操作。该操作由专门的线程(ReferenceHandle 线程处理),我重点将 ReferenceHandle线程的源代码贴出已供分析。
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) { // 如果pengding不为空,则取出pengding 的第一个引用,然后重新设置pengding 的值(为原来的pending.next,见如下代码 a,b,c)
r = pending; // a 将pending取出,准备入队操作
Reference rn = r.next; // b 先获取原先pending 的 next
pending = (rn == r) ? null : rn; // c 如果pending的next等于本身,则设在pending为空,否则为链表的下一个。// 从这里更加看出 pending 是后进先出队列。
r.next = r;
} else { // 如果 pending 为空,则线程阻塞,等待垃圾回收器添加新的引用到 pending链表中
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);
}
}
}
参考了如下几篇非常优秀的博文,再次表示感谢:
http://hongjiang.info/java-referencequeue/
http://blog.csdn.net/wangyuexiongqi/article/details/38588331