题记:
前几天讨论到WeakHashMap(这个是个弱引用的Map,用于缓存,弱引用的特点是随时被GC回收),WHM在每次put()会间接地调用expungeStaleEntries()来从去除实体。今天看到Hudon的WeakLogHandler的应用就怀着学习的目的来看看WeakRefence。(软引用在内存不足的时候被GC回收,弱引用可能在每次都被GC回收,她们的机制或原理是什么呢?我总是很好奇)
分析:
粗略看了下设计,有很多疑点不懂:
1、pending对象的用处?
2、ReferenceHandler线程什么时候notify呢?
3、到底哪些对象会被GC回收呢?
查了点资料后:http://www.ibm.com/developerworks/cn/java/i-garbage2/index.html
才有了点领悟。
“在 Java 1.2.2 中,整个引用处理发生很大的改变。其目标是平等地对待所有的引用,并以相同的方式处理它们。因此,我们实际上在堆上创建两个单独的对象 —— 对象本身和一个单独的 “引用” 对象。可选地,引用对象可以与一个队列相关联,当 referent 变得不可获得时,可以将引用对象添加到该队列中。”
所有引用都有个“引用队列”,也就是代码里的queue,ReferenceHandler 线程负责把reference入队列,如果未“标记”或者该引用没有指向任何Queue,则等待GC回收。
这里所谓的“标记”是指GC的标记阶段,也就是GC标记所有活动的对象,从效率上考虑,GC不会去标记unreachable的对象,而未“标记”的对象就是“垃圾”了。
所以作为WeakReference引用就不会被“标记”(register),从而会被GC回收掉,这点与Soft引用不同,Soft引用被标记但在内存不足的情况下,被特殊处理。
有分析不正确的地方,还请指正,也是处于学习阶段:)
弱引用Logger例子:
public final class WeakLogHandler extends Handler {
private final WeakReference<Handler> target;
private final Logger logger; // Logger的管理对象
public WeakLogHandler(Handler target, Logger logger) {// 典型的把管理对象传入
this.logger = logger;
logger.addHandler(this);// 把干活对象加入管理对象中。
this.target = new WeakReference<Handler>(target); // 新建弱引用,且未指定queue
}
public void publish(LogRecord record) {
Handler t = resolve();
if(t!=null)
t.publish(record);
}
public void flush() {
Handler t = resolve();
if(t!=null)
t.flush();
}
public void close() throws SecurityException {
Handler t = resolve();
if(t!=null)
t.close();
}
private Handler resolve() {
Handler r = target.get(); // get引用,判断是否已经被回收
if(r==null)
logger.removeHandler(this);
return r;
}
@Override
public void setFormatter(Formatter newFormatter) throws SecurityException {
super.setFormatter(newFormatter);
Handler t = resolve();
if(t!=null)
t.setFormatter(newFormatter);
}
}
看看小兵WeakRefence的定义:
public class WeakReference<T> extends Reference<T> {
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
看到基本共享父类Reference。(莫非JAVA引用默认就是弱引用?)
看看Reference类定义:
public abstract class Reference<T> {
private T referent; /* Treated specially by GC */
ReferenceQueue<? super T> queue; // 从后面分析来看,这个就是引用队列了,被标记的引用入队列
Reference next; // 指向下一个引用
transient private Reference<T> discovered; /* used by VM */
/* Object used to synchronize with the garbage collector. The collector
* must acquire this lock at the beginning of each collection cycle. It is
* therefore critical that any code holding this lock complete as quickly
* as possible, allocate no new objects, and avoid calling user code.
*/
static private class Lock { };
private static Lock lock = new Lock(); // GC线程在回收的时候的锁。
/* List of References waiting to be enqueued. The collector adds
* References to this list, while the Reference-handler thread removes
* them. This list is protected by the above lock object.
*/
private static Reference pending = 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;// 弱引用就是一个NULL的引用队列
}
/* High-priority thread to enqueue pending References
*/
private static class ReferenceHandler extends Thread {// 注意这里是static线程,且优先级最高。
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
for (;;) {
Reference r;
synchronized (lock) {
if (pending != null) {
r = pending;
Reference rn = r.next;
pending = (rn == r) ? null : rn;
r.next = r;
} else {
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); // 构造函数里面未指定queue,则指向NULL queue
}
}
}
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();
}
/* -- Referent accessor and setters -- */
/**
* Returns this reference object's referent. If this reference object has
* been cleared, either by the program or by the garbage collector, then
* this method returns <code>null</code>. 如果引用被程序或GC回收,则返回null
*/
public T get() {
return this.referent;
}
/**
* Clears this reference object. Invoking this method will not cause this
* object to be enqueued.
*/
public void clear() {
this.referent = null;
}
/* -- Queue operations -- */
/**
* Tells whether or not this reference object has been enqueued, either by
* the program or by the garbage collector. If this reference object was
* not registered with a queue when it was created, then this method will
* always return <code>false</code>.
* 当前引用是否被程序或者GC入队列,如果创建的时候未注册,则返回false
*/
public boolean isEnqueued() {
/* In terms of the internal states, this predicate actually tests
whether the instance is either Pending or Enqueued */
synchronized (this) {
return (this.queue != ReferenceQueue.NULL) && (this.next != null);
}
}
/**
* Adds this reference object to the queue with which it is registered,
* if any.
*/
public boolean enqueue() {
return this.queue.enqueue(this);
}
/* -- Constructors -- */
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
}