定义
Reference是所有引用类型的父类,定义了引用的公共行为和操作。

reference指代引用对象本身,referent指代reference引用的对象,下文介绍会以reference,referent形式出现。
说明
Reference类与垃圾回收是密切配合的,所以该类不能被直接子类化。简单来讲,Reference的继承类都是经过严格设计的,甚至连成员变量的先后顺序都不能改变,所以在代码中直接继承Reference类是没有任何意义的。但是可以继承Reference类的子类。
例如:Finalizer 继承自 FinalReference,Cleaner 继承自 PhantomReference
构造函数
Reference类中有两个构造函数,一个需要传入引用队列,另一个则不需要。
这个队列的意义在于增加一种判断机制,可以在外部通过监控这个队列来判断对象是否被回收。如果一个对象即将被回收,那么引用这个对象的reference对象就会被放到这个队列中。通过监控这个队列,就可以取出这个reference后再进行一些善后处理。
如果没有这个队列,就只能通过不断地轮询reference对象,通过get方法是否返回null( phantomReference对象不能这样做,其get方法始终返回null,因此它只有带queue的构造函数 )来判断对象是否被回收。
这两种方法均有相应的使用场景,具体使用需要具体情况具体分析。比如在weakHashMap中,就通过查询queue的数据,来判定是否有对象将被回收。而ThreadLocalMap,则采用判断get()是否为null来进行处理。
/* -- Constructors -- */ Reference(T referent) { this(referent, null); } Reference(T referent, ReferenceQueue<? super T> queue) { this.referent = referent; this.queue = (queue == null) ? ReferenceQueue.NULL : queue; }内部成员
Reference类内部有这么几个成员变量:
referent:保存reference指向的对象。
private T referent;queue:引用对象关联的引用队列。是对象即将被回收时所要通知的队列。当对象将被回收时,reference对象( 而不是referent引用的对象 )会被放到queue里面,然后外部程序即可通过监控这个queue拿到相应的数据了。
这里的queue( 即,ReferenceQueue对象 )名义上是一个队列,实际内部是使用单链表来表示的单向队列,可以理解为queue就是一个链表,其自身仅存储当前的head节点,后面的节点由每个reference节点通过next来保持即可。
volatile ReferenceQueue<? super T> queue;next:指向下一个引用,Reference是一个单链表的结构。
Reference next;discovered:表示要处理的对象的下一个对象。
/* 当处于active状态: discovered链表中下一个待处理对象 * 当处于pending状态: pending列表中的下一个对象 * 其它状态: NULL */ transient private Reference<T> discovered;lock:内部同步锁对象。用作在操作pending链表时的同步对象。注意这是一个静态对象,意味着所有Reference对象共用同一个锁。
static private class Lock { } private static Lock lock = new Lock();pending:等待添加到queue中的元素链表。注意这是一个静态对象,意味着所有Reference对象共用同一个pending队列。
/* 用来保存那些需要被放入队列中的reference,收集器会把引用添加到这个列表里来, * Reference-handler线程会从中移除它们。 * 这个列表由上面的lock对象锁进行保护。列表使用discovered字段来链接它的元素。 */ private static Reference<Object> pending = null;::: warning 说明
queue队列使用next来查找下一个reference,pending队列使用discovered来查找下一个reference。
:::
Reference状态
在Reference类中,有一段很长的注释,来对内部对象referent的状态进行了说明。
Active:
reference如果处于此状态,会受到垃圾处理器的特殊处理。当垃圾回收器检测到referent已经更改为合适的状态后(没有任何强引用和软引用关联),会在某个时间将实例的状态更改为Pending或者Inactive。具体取决于实例是否在创建时注册到一个引用队列中。
在前一种情况下(将状态更改为Pending),他还会将实例添加到pending-Reference列表中。新创建的实例处于活动状态。Pending:
实例如果处于此状态,表明它是pending-Reference列表中的一个元素,等待被Reference-handler线程做入队处理。未注册引用队列的实例永远不会处于该状态。Enqueued:
实例如果处于此状态,表明它已经是它注册的引用队列中的一个元素,当它被从引用队列中移除时,它的状态将会变为Inactive,未注册引用队列的实例永远不会处于该状态。Inactive:
实例如果处于此状态,那么它就是个废实例了(滑稽),它的状态将永远不会再改变了。
所以实例一共有四种状态,Active(活跃状态)、Pending(半死不活状态)、Enqueued(濒死状态)、Inactive(凉凉状态)。当然,Pending和Enqueued状态是引用实例在创建时注册了引用队列才会有。
一个reference处于Active状态时,表示它是活跃正常的,垃圾回收器会监视这个引用的referent,如果扫描到它没有任何强引用关联时就会进行回收判定了。
如果判定为需要进行回收,则判断其是否注册了引用队列,如果有的话将reference的状态置为pending。当reference处于pending状态时,表明已经准备将它放入引用队列中,在这个状态下要处理的对象将逐个放入queue中。在这个时间窗口期,相应的引用对象为pending状态。
当它进入到Enqueued状态时,表明已经引用实例已经被放到queue当中了,准备由外部线程来轮询获取相应信息。此时引用指向的对即将被垃圾回收器回收掉了。
当它变成Inactive状态时,表明它已经凉透了,它的生命已经到了尽头。不管你用什么方式,也救不了它了。
JVM中并没有显示定义这样的状态,而是通过next和queue来进行判断。
Active:如果创建Reference对象时,没有传入ReferenceQueue,queue=ReferenceQueue.NULL。如果有传入,则queue指向传入的ReferenceQueue队列对象。next == null; Pending:queue为初始化时传入ReferenceQueue对象;next == this; Enqueue:queue == ReferenceQueue.E
