Java源码阅读------Reference

描述

Reference是PhantomReference(虚引用),WeakReference(弱引用),SoftReference(软引用)的父类。位于java.lang.ref包中,是一个使用泛型的抽象类。它定义了所有引用对象的一般操作,与垃圾回收机制(gc)息息相关。

四种状态

每一个Reference实例都处在四种可能的内在状态之一。

Active

/*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.
 */

活跃状态,新创建的实例,或是还有强引用的实例是处于Active状态,在gc处理时,会根据是否注册引用队列来区分处理。注册了的会将其放到pending状态,没有注册的直接转到Inactive。

Pending

/*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-handler这个线程的处理,将其入队进入引用队列ReferenceQueue中,进入Enqueued状态,没有注册引用队列的自然不会进入这一状态。

Enqueued

/*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.
 */

队内状态,在这一状态下,引用对象将会处于ReferenceQueue中,如果从中移除后将会进入Inactive状态,同理没有注册ReferenceQueue的引用对象无法进入这一状态。

Inactive

/*Inactive: Nothing more to do.  Once an instance becomes Inactive its
 *     state will never change again.
 */ 

不活跃状态,最终状态,位于这一状态的实例将会被gc回收。

内部实现

referent

private T referent;         /* Treated specially by GC */

对内部对象的保存引用,使用了泛型,gc会特别处理。

public T get() {
    return this.referent;
}

相关的get方法获取了包装的引用。

public void clear() {
	this.referent = null;
}

使用clear方法可以对referent 清理,之后其将不会再被加入引用队列,gc在回收清理时并不执行该方法,而是更直接的清理。

queue

volatile ReferenceQueue<? super T> queue;

对于需要通知机制的,用于储存传入的引用队列,使用通配符泛型,内部可以是相关的子类。

public boolean isEnqueued() {
	return (this.queue == ReferenceQueue.ENQUEUED);
}

这一方法可以判断queue中是否含有引用。

public boolean enqueue() {
    return this.queue.enqueue(this);
}

将注册了引用队列的引用加入其对应的队列中。

构造函数

有两种构造函数分别实现需要引用队列的和不需要引用队列的。

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与ENQUEUED分别表示没有设置引用队列和已经入队的两种状态。

private static class Null<S> extends ReferenceQueue<S> {
	boolean enqueue(Reference<? extends S> r) {
		return false;
	}
}

static ReferenceQueue<Object> NULL = new Null<>();
static ReferenceQueue<Object> ENQUEUED = new Null<>();

初始化的静态处理

线程

线程:首先,线程不是进程,一个进程有很多子线程,线程是进程中的一个执行流程,只是轮换执行的过程不是同时。多进程是操作系统同时运行多个程序的方法,多线程是程序同时运行多个处理的方法,这里的同步是我们的感觉同步,实际上还是碎片化时间轮流处理。在java中以Thread类实现。
线程组:用于管理线程,是线程的组合,除了初始线程组外,每个线程组都有一个父线程组,所有的线程组呈树状分布,线程组中除了包含线程还会包含线程组。
线程优先级:根据线程处理的事务不同给它们分配不同的优先级,优先级越高越可能被执行,在Thread中定义最小(MIN_PRIORITY)为1,最大(MAX_PRIORITY)为10,默认的优先级(NORM_PRIORITY)为5。
线程分类:一般分为守护线程与用户线程,两者的区别主要体现在java虚拟机的处理上,守护线程依赖非守护线程而存在,如果不存在非守护线程只剩下守护线程,那么虚拟机会直接将其终止并退出(工作完成)。

静态处理

static {
	ThreadGroup tg = Thread.currentThread().getThreadGroup();
    for (ThreadGroup tgn = tg;
    tgn != null;
    tg = tgn, tgn = tg.getParent());
    Thread handler = new ReferenceHandler(tg, "Reference Handler");
    /* If there were a special system-only priority greater than
    * MAX_PRIORITY, it would be used here
    */
    handler.setPriority(Thread.MAX_PRIORITY);
    handler.setDaemon(true);
    handler.start();
    // provide access in SharedSecrets
    SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
        @Override
		public boolean tryHandlePendingReference() {
			return tryHandlePending(false);
		}
	});
}

首先获取了当前正在执行的线程的线程组。之后使用循环获得根线程组,应该是System线程组。之后初始化了一个ReferenceHandler线程,这个是Reference的一个内部类,将名为Reference Handler的线程注册到根线程组中,并设置优先级为最高的10,并设置为守护进程运行,必须在运行之前设置。
最后说一下SharedSecrets类,这个类的实现只是包含了许多接口的get与set,简单的说是实现了私有方法的共享,而不必使用反射去实现,在这里其实现了JavaLangRefAccess这个接口,共享了tryHandlePending这一私有方法。

ReferenceHandler

ReferenceHandler是一个静态内部类,继承自Thread是一个线程,负责将处于pending状态的引用加入引用队列。

构造函数

ReferenceHandler(ThreadGroup g, String name) {
	super(g, name);
}

调用Thread中的方法将线程加入到线程组中。

静态处理

static {
	// pre-load and initialize InterruptedException and Cleaner classes
	// so that we don't get into trouble later in the run loop if there's
	// memory shortage while loading/initializing them lazily.
	ensureClassInitialized(InterruptedException.class);
	ensureClassInitialized(Cleaner.class);
}

使用ensureClassInitialized方法完成InterruptedException与Cleaner两个类的初始化加载。

ensureClassInitialized

很简单的实现,通过传入的类获取类名加载对应的类,参数中的true为实现初始化,getClassLoader为获取传入的类中的类加载器。

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);
    }
}

run

这个方法大家应该很熟悉,线程的运行方法包含运行进行的操作。

public void run() {
	while (true) {
		tryHandlePending(true);
	}
}

实现也很直观就是跑tryHandlePending这个函数。

tryHandlePending

应该算是这个类中比较有意思的一个函数了,负责将处于Pending状态的Reference加入到引用队列queue中。若返回为true表示还有处于Pending状态的Reference,为false则没有,可以通过返回值来操控其运行。waitForNotify参数表示是否等待Notify方法,当没有Pending状态的引用时waitForNotify为true则等待Notify方法或线程的中断interrupted,为false就立即结束。

Object锁

static private class Lock { }
private static Lock lock = new Lock();

这里定义了一个Object用于线程同步,具体可以参考Object中的notify与wait方法Java源码阅读------Object

数据结构

private static Reference<Object> pending = null;
transient private Reference<T> discovered;  /* used by VM */
Reference next;

在实现中出现了两个数据结构,一个是负责描述入队后引用的单向列表由next指向下一个Reference对象,具体的是应用在ReferenceQueue实现,还有一个是Pending队列用于存储所有Pending状态的Reference,由discovered与pending实现分别指向队列首部与相应节点的下一个元素。

详细实现

static boolean tryHandlePending(boolean waitForNotify) {
	Reference<Object> r;
    Cleaner c;
    try {
    	synchronized (lock) {
        	if (pending != null) {
            	r = pending;
                // 'instanceof' might throw OutOfMemoryError sometimes
                // so do this before un-linking 'r' from the 'pending' chain...
                c = r instanceof Cleaner ? (Cleaner) r : null;
                // unlink 'r' from 'pending' chain
                pending = r.discovered;
                r.discovered = null;
            } else {
                // The waiting on the lock may cause an OutOfMemoryError
                // because it may try to allocate exception objects.
                if (waitForNotify) {
                	lock.wait();
                }
                // retry if waited
                return waitForNotify;
            }
        }
	} catch (OutOfMemoryError x) {
    	// 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...
        Thread.yield();
        // retry
        return true;
    } catch (InterruptedException x) {
        // retry
        return true;
    }
    // Fast path for cleaners
    if (c != null) {
        c.clean();
        return true;
    }
	ReferenceQueue<? super Object> q = r.queue;
    if (q != ReferenceQueue.NULL) q.enqueue(r);
    return true;
}

判断pending是否为空,为空则根据waitForNotify参数来判断是否立即返回。若不为空,用r取出Pending中的首部元素,重新将Pending指向其下一个元素,判断r是否为Cleaner类方便进行单独的处理,如果是就用c来存储,跳出同步操作,根据c中的数据进行单独的Cleaner清理,最后将获得的Pending状态的引用加入到对应的ReferenceQueue中。注意在同步操作部分可能会出现内存溢出,所以使用了try-catch,出现异常时默认返回true。

小结

Reference的使用很灵活,其运行过程与gc处理息息相关,源码中的思想还是很值得研究的。

猜你喜欢

转载自blog.csdn.net/sinat_36945592/article/details/87099185