精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-06-14
最后修改:2010-12-15
题记: 前几天讨论到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; } }
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-09-19
Nice,最近也在看弱引用,边看边翻译。
|
|
返回顶楼 | |
发表时间:2010-12-08
最后修改:2010-12-08
今天重读了下Reference,先回答自己提出的问题。
1.private static Reference pending = null;,pending对象是一个static对象,他是等待入引用队列的引用。 2.private static class ReferenceHandler extends Thread,看到ReferenceHandler其实是个静态全局线程,优先级最高,他的工作就是一发现pending引用不为NULL,就进行入引用队列操作,可以猜测,当pending不为NULL时,ReferenceHandler被吹起来。 3.其实Reference就是一个弱引用的实现,判断一个对象引用是否在引用队列里,或者说GC判断对象是否回收,靠Reference里的isEnqueued方法进行判断: 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); } } 所以可以猜测每个Reference的ReferenceQueue应该在构造函数时被赋值,发现果然: Reference(T referent, ReferenceQueue<? super T> queue) { this.referent = referent; this.queue = (queue == null) ? ReferenceQueue.NULL : queue; } 对于入引用队列的线程,有个很巧妙的设计: r = pending; Reference rn = r.next; pending = (rn == r) ? null : rn; r.next = r; 把next重新指向pending,这样不断循环入队列。 |
|
返回顶楼 | |
发表时间:2010-12-14
最后修改:2010-12-14
继续呗:
GC采用很多策略进行GC回收。其中为了消除内存碎片,采用“复制”策略: JVM把内存拆分了3大区域,就是GC中常说的:eden、survivor、old,把eden区域“存活”的对象copy到survivor区域,然后清空eden所有内存。 另外GC根据“根搜索”策略来判断对象是否“存活”,我不确定是否跟上面提及到的ReferenceQueue有关。 希望有大大讲解下呗~ |
|
返回顶楼 | |
发表时间:2010-12-15
JE是越来越冷清,没人回帖了~
|
|
返回顶楼 | |
浏览 6156 次