我的原则:先会用再说,内部慢慢来。
学以致用,根据场景学源码
文章目录
- 一、架构
- 1.1 代码架构图
- 1.2 状态流程图
- 1.2.1 简易流程图
- 1.2.2 UML流程图 (单ReferenceQueue)
- 1.2.3 UML流程图 (多ReferenceQueue)
- 1.2.4 Reference对象的状态通过成员变量next和queue来判断
- 1.2.5 未注册 ReferenceQueue
- 二、ReferenceQueue 源码剖析
- 2.1 ReferenceQueue 类
- 2.1.1 Null 内部类
- 2.2 enqueue 方法
- 2.3 poll 方法
- 2.4 remove 方法
- 2.5 forEach() 方法
- 三、Reference 源码剖析
- 3.1 Reference 类
- 3.2 Reference 的四种状态
- 3.2.1 状态Active
- 3.2.2 状态Pending
- 3.2.3 状态Enqueued
- 3.2.4 状态Inactive
- 3.2.5 状态总结
- 3.2.6 流程总结
- 3.3 内部类ReferenceHandler
- 3.4 Reference#tryHandlePending() 方法
- 3.5 Reference的static代码块
- 3.6 Reference 构造方法
- 四、番外篇
状态 | Next | Queue | discovered |
---|---|---|---|
Active | null | referenceQueue | 下一个要回收的 reference(gc决定)(假如是最后一个,那么就是 this ) |
Pending | this | referenceQueue | 下一个要进入队列的 reference (假如是最后一个,那么就是 this ) |
Enqueued | 队列中下一个节点(假如是最后一个,next = this) | ReferenceQueue.ENQUEUED | null |
Inactive | this | ReferenceQueue.NULL | null |
在 Active 与 Pending 状态下的, discovered 就相当于 next。
如果未注册 Queue,那么就只有 Active 与 Inactive 状态。
=== 点击查看top目录 ===
ReferenceQueue队列是一个单向链表,ReferenceQueue里面只有一个head 成员变量持有队列的队头。后进先出的队列,其实是个就是个栈!!!
ReferenceQueue和 Reference 类都是 jdk1.2 的时候出的,所以也就不可能继承jdk1.5出来的Queue接口
package java.lang.ref;
import java.util.function.Consumer;
public class ReferenceQueue<T> {
public ReferenceQueue() { }
private static class Null<S> extends ReferenceQueue<S> {
boolean enqueue(Reference<? extends S> r) {
return false; //内部类,它是用来做状态识别的,重写了enqueue入队方法,永远返回false,所以它不会存储任何数据,见后面的NULL和ENQUEUED两个标识成员变量
}
}
/*
状态State:Inactive
1. 当 Reference对象创建时没有指定 queue,设置为 NULL
2. 该 Reference 被拉出队列的时候,设置为 NULL
*/
static ReferenceQueue<Object> NULL = new Null<>();
/*
状态State:ENQUEUED
Reference已经被ReferenceHander线程从pending队列移到queue里面时。设置为 ENQUEUED
所以ENQUEUED状态不可能在调用 enqueue 方法
*/
static ReferenceQueue<Object> ENQUEUED = new Null<>();
static private class Lock { };
// 锁对象,只是用来这个类加锁用
private Lock lock = new Lock();
// 头节点(有存放数据)
private volatile Reference<? extends T> head = null;
// 队列真实长度
private long queueLength = 0;
// 如果Reference创建时没有指定队列或Reference对象已经在队列里面了,则直接返回
boolean enqueue(Reference<? extends T> r) { }
private Reference<? extends T> reallyPoll() {}
public Reference<? extends T> poll() {
if (head == null)
return null;
synchronized (lock) {
return reallyPoll();
}
}
//将头部第一个对象移出队列并返回,如果队列为空,则等待timeout时间后,返回null,这个方法会阻塞线程
public Reference<? extends T> remove(long timeout){}
//将队列头部第一个对象从队列中移除出来,如果队列为空则直接返回null(此方法不会被阻塞)
public Reference<? extends T> remove(){return remove(0);}
void forEach(Consumer<? super Reference<? extends T>> action) {}
}
private static class Null<S> extends ReferenceQueue<S> {
boolean enqueue(Reference<? extends S> r) {
return false;
}
}
=== 点击查看top目录 ===
boolean enqueue(Reference<? extends T> r) {
synchronized (lock) {
// Check that since getting the lock this reference hasn't already been
// enqueued (and even then removed)
ReferenceQueue<?> queue = r.queue;
// 如果Reference创建时没有指定队列或Reference对象已经在队列里面了,则直接返回
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this; //!!! 只有r的队列是当前队列才允许入队
r.queue = ENQUEUED; //将r的queue设置为ENQUEUED状态,标识Reference已经入队
r.next = (head == null) ? r : head; // 往头部插 ,类似于栈结构
head = r;
queueLength++;
if (r instanceof FinalReference) {
//如果r是一个FinalReference实例,那么将FinalReference数量也+1
sun.misc.VM.addFinalRefCount(1);
}
lock.notifyAll();
return true;
}
}
=== 点击查看top目录 ===
public Reference<? extends T> poll() {
if (head == null)
return null;
synchronized (lock) {
return reallyPoll();
}
}
private Reference<? extends T> reallyPoll() { /* Must hold lock */
Reference<? extends T> r = head; // 弹出 head ,从头部取,类似于栈结构,后进先出
if (r != null) {
@SuppressWarnings("unchecked")
Reference<? extends T> rn = r.next;
head = (rn == r) ? null : rn;
r.queue = NULL; // 打个出队列标志 NULL
r.next = r;
queueLength--;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(-1);
}
return r;
}
return null;
}
=== 点击查看top目录 ===
// 删掉头节点,队列是空的话,等待N秒
public Reference<? extends T> remove(long timeout)
throws IllegalArgumentException, InterruptedException
{
if (timeout < 0) {
throw new IllegalArgumentException("Negative timeout value");
}
synchronized (lock) {
Reference<? extends T> r = reallyPoll();
if (r != null) return r;
long start = (timeout == 0) ? 0 : System.nanoTime();
for (;;) {
lock.wait(timeout);
r = reallyPoll();
if (r != null) return r;
if (timeout != 0) {
long end = System.nanoTime();
timeout -= (end - start) / 1000_000;
if (timeout <= 0) return null;
start = end;
}
}
}
}
// 删掉头节点,队列是空的话,一直等待下去, 等待notify
public Reference<? extends T> remove() throws InterruptedException {
return remove(0);
}
=== 点击查看top目录 ===
这是jdk1.8 新增的接口,传入一个 Consumer,调用 foreach 消费队列中全部的Reference。
void forEach(Consumer<? super Reference<? extends T>> action) {
for (Reference<? extends T> r = head; r != null;) {
action.accept(r);
@SuppressWarnings("unchecked")
Reference<? extends T> rn = r.next;
if (rn == r) {
if (r.queue == ENQUEUED) {
// still enqueued -> we reached end of chain
r = null;
} else {
// already dequeued: r.queue == NULL; ->
// restart from head when overtaken by queue poller(s)
r = head;
}
} else {
// next in chain
r = rn;
}
}
}
=== 点击查看top目录 ===
这是 referece对象的抽象base类,这个类定义了所有 reference 对象的通用方法,因为 reference 对象跟 GC垃圾。
/**
* Abstract base class for reference objects. This class defines the
* operations common to all reference objects. Because reference objects are
* implemented in close cooperation with the garbage collector, this class may
* not be subclassed directly.
*
* @author Mark Reinhold
* @since 1.2
*/
// 这是 referece对象的抽象base类,这个类定义了所有 reference 对象的通用方法,因为 reference 对象跟 GC垃圾收集器密切合作。
public abstract class Reference<T> {
private T referent; /* Treated specially by GC */
@SuppressWarnings("rawtypes")
volatile Reference next;
static private class Lock { }
//lock成员变量是pending队列的全局锁,
//1. tryHandlePending 加锁出队列
//2. jvm垃圾回收器线程入pending队列,往pending里面添加Reference对象
private static Lock lock = new Lock();
//pending队列, pending成员变量与后面的discovered对象一起构成了一个pending单向链表,注意这个成员变量是一个静态对象,所以是全局唯一的,pending为链表的头节点,discovered为链表当前Reference节点指向下一个节点的引用
// 这个队列是由jvm的垃圾回收器构建的,当对象除了被reference引用之外没有其它强引用了,jvm的垃圾回收器就会将指向需要回收的对象的Reference都放入到这个队列里面
private static Reference<Object> pending = null;
//与成员变量pending一起组成pending队列,指向链表当前节点的下一个节点
transient private Reference<T> discovered; /* used by VM */
//ReferenceQueue队列,ReferenceQueue并不是一个链表数据结构,它只持有这个链表的表头对象header,这个链表是由Refence对象里面的next成员变量构建起来的,next也就是链表当前节点的下一个节点(只有next的引用,它是单向链表)
volatile ReferenceQueue<? super T> queue;
//以下是ReferenceHander线程初始化并启动的操作
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler"); //线程名称为Reference Handler
handler.setPriority(Thread.MAX_PRIORITY); //线程有最高优先级
handler.setDaemon(true);//设置线程为守护线程;
handler.start();
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
public boolean tryHandlePendingReference() {
return tryHandlePending(false);
}
});
}
Reference(T referent) {this(referent, null);}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
static boolean tryHandlePending(boolean waitForNotify) {
//从pending中移除的Reference对象
Reference<Object> r;
Cleaner c;
try {
//此处需要加全局锁,因为除了当前线程,gc线程也会操作pending队列。GC 往pending队列塞Reference,该方法是取出来
synchronized (lock) {
//如果pending队列不为空,则将第一个Reference对象取出
if (pending != null) {
r = pending; //缓存pending队列头节点
c = r instanceof Cleaner ? (Cleaner) r : null;
pending = r.discovered; //将头节点指向discovered,discovered为pending队列中当前节点的下一个节点,这样就把第一个头结点出队了
r.discovered = null; //将当前节点的discovered设置为null;当前节点出队,不需要组成链表了;
} else {
//如果pending队列为空
if (waitForNotify) { //若要等待被唤醒,那么 wait,
lock.wait();
}
return waitForNotify; //直接返回
}
}
} catch (OutOfMemoryError x) {
Thread.yield();
return true;
} catch (InterruptedException x) {
return true;
}
if (c != null) {
// 如果从pending队列出队的r是一个Cleaner对象,那么直接执行其clean()方法执行清理操作;
c.clean();
//注意这里直接 return true返回,这里已经不往下执行了,所以Cleaner对象是不会进入到队列里面的,给它设置ReferenceQueue的作用是为了让它能进入Pending队列后被ReferenceHander线程处理;
return true;
}
ReferenceQueue<? super Object> q = r.queue;
//将对象放入到它自己的ReferenceQueue队列里
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
public T get() {return this.referent;}
public void clear() {this.referent = null;}
public boolean isEnqueued() {return (this.queue == ReferenceQueue.ENQUEUED);}
public boolean enqueue() {return this.queue.enqueue(this);}
private static class ReferenceHandler extends Thread {
private static void ensureClassInitialized(Class<?> clazz) {
try {
Class.forName(clazz.getName(), true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
}
}
static {
ensureClassInitialized(InterruptedException.class);
ensureClassInitialized(Cleaner.class);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
//从源码中可以看出,这个线程在Reference类的static构造块中启动,并且被设置为最高优先级和daemon状态。此线程要做的事情就是不断的的检查pending是否为null,如果pending不为null,则将pending进行enqueue,否则线程进行wait状态。
public void run() {
while (true) {
tryHandlePending(true);
}
}
}
}
从注释可以知道,该 Reference 类跟 GC回收密切相关。
=== 点击查看top目录 ===
instance 实例对象就是一个 reference 对象
Active: Subject to special treatment by the garbage collector. Some
time after the collector detects that the reachability of the referent has changed to the appropriate state, it changes the instance’s state to either Pending or Inactive, depending upon
whether or not the instance was registered with a queue when it was
created. In the former case it also adds the instance to the
pending-Reference list. Newly-created instances are Active.
该状态下的 Reference 会收到 GC 收集器的特别对待。有时候,当 GC 收集器检测到 referent 的可达性变更为其他合适状态的时候。它会把 instance 的状态改变为 Pending 或者 Inactive,这取决于你创建 instance 的时候,
有没有注册 ReferenceQueue,如果有(前者),那么会将这个 instance 加到 pending-Reference 链表里面去。(状态改成 Pending)后者的话,就是没注册 ReferenceQueue 的话,那么状态变成(Inactive)
新创建的对象实例状态都是 Active
Pending: An element of the pending-Reference list, waiting to be
enqueued by the Reference-handler thread. Unregistered instances
are never in this state.
一个 pending-Reference list中的元素,等待 Reference-handler线程 安排进队列之前就是这个 Pending 状态。没有注册的 instance 永远不可能到达这一状态。
Enqueued: An element of the queue with which the instance was
registered when it was created. When an instance is removed from
its ReferenceQueue, it is made Inactive. Unregistered instances are
never in this state.
当创建 Reference 的时候有注册 queue 的元素。当一个 instance 从 ReferenceQueue 中 remove 的时候,
Reference 状态变成 Inactive。没有注册ReferenceQueue的不可能到达这一状态的。
Inactive: Nothing more to do. Once an instance becomes Inactive its
state will never change again.
没啥事可做。一旦一个 instance 变成 Inactive 状态,那么不会再改变了。
=== 具体看状态流程图 ===
=== 点击查看top目录 ===
private static class ReferenceHandler extends Thread {
//确保类已经加载到 JVM 里面去了
private static void ensureClassInitialized(Class<?> clazz) {
try {
Class.forName(clazz.getName(), true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
}
}
static {
// 预先加载 InterruptedException 与 Cleaner 类,避免懒加载的时候,外面已经出现了内存问题。
ensureClassInitialized(InterruptedException.class);
ensureClassInitialized(Cleaner.class);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
while (true) { // 死循环
tryHandlePending(true);
}
}
}
=== 点击查看top目录 ===
static boolean Reference#tryHandlePending(boolean waitForNotify) {
Reference<Object> r;
Cleaner c;
try {
// 加一个锁,因为除了这里在操作出队列, GC也在网pending队列里面塞数据
synchronized (lock) {
// 如果 pending 队列不为空
if (pending != null) {
r = pending;
// 对 Cleaner 特殊处理
c = r instanceof Cleaner ? (Cleaner) r : null;
pending = r.discovered; // 指向pending的下一个 Reference
r.discovered = null; // 置空
} else {
// 如果需要等待唤醒,那么就进去睡觉
if (waitForNotify) {
lock.wait();
}
return waitForNotify;
}
}
} catch (OutOfMemoryError x) { // 这里捕获了OOM,表示还先别终止 JVM,再给他一次机会
// Give other threads CPU time so they hopefully drop some live references
// and GC reclaims some space.
// Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
// persistently throws OOME for some time...
// 给其他线程一些cpu时间使得他们能够丢掉一些活着的 reference 和让cpu回收点空间。
// 并且防止在继承 Cleaner的时候 cpu 内部自旋
Thread.yield();
// retry
return true;
} catch (InterruptedException x) { // 就算被打断又怎样
// retry
return true;
}
// Fast path for cleaners
// 如果是 cleaner 的话,那么不进入队列,直接调用 clean方法,然后回收
if (c != null) {
c.clean();
return true;
}
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r); // queue有指定的话,入队列
return true;
}
=== 点击查看top目录 ===
//以下是ReferenceHander线程初始化并启动的操作
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler"); //线程名称为Reference Handler
handler.setPriority(Thread.MAX_PRIORITY); //线程有最高优先级
handler.setDaemon(true);//设置线程为守护线程;
handler.start(); // 启动内部类 Handler
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
public boolean tryHandlePendingReference() {
return tryHandlePending(false);
}
});
}
Reference 类的 static 块,handler.start(); 启动了一个幽灵线程,这个线程就是 ReferenceHandler 线程类 ,它所做的,就是不断轮训遍历 pending 队列,然后丢入到 referenceQueue 队列里面去,即使遇到 OOM 或者 InterruptedException, 全部忽略。
=== 点击查看top目录 ===
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
下一章节:【JAVA Reference】Cleaner 源码剖析(三)
上一章节:【JAVA Reference】Java的强引用、软引用、弱引用和虚引用(一)