偶然接触到 MemoryCache ,其中使用了 ReferenceQueue 作为失效缓存监听,不明里面对 Reference 的处理逻辑,遂稍作梳理。
Reference 类型
Java 中定义了四种引用对象类型,分别为
- StrongReference:强引用,GC时不会被回收
- SoftReference:软引用,内存足够时,GC不会回收;内存不足时,GC会将其回收
- WeakReference:弱引用,GC会回收
- PhantomReference:幽灵引用,待深入
Reference 状态
- Active 活跃:对象创建后的初始状态
- Pending 待定:当 queue 不为空且GC判定对象可达性发生变化
- Enqueued 待回收:由内部线程转化
- Inactive 非活跃:当 queue 为空且GC判定对象可达性发送变化
几个特殊的常量:
- ReferenceQueue.NULL:表示无指定queue
- ReferenceQueue.ENQUEUED:表示当前节点已入queue,当前queue被指定该值
通信媒介 - ReferenceQueue
在创建 Reference 时可关联指定 ReferenceQueue,然后可在与 Reference 关联的 ReferenceQueue 中获取到即将被回收的 Reference 对象,用于后续相关操作,如删除对应的缓存数据。
所以 ReferenceQueue 是 JVM GC 与 代码层的通信介质,用于监听对象引用的可达性变化。
内部线程 - ReferenceHandler
ReferenceHandler 负责维护各引用的状态。
本质上是个 Thread 。
/* High-priority thread to enqueue pending References
*/
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 {
// 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);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
while (true) {
// 循环将 pending 引用移入 queue 中
tryHandlePending(true);
}
}
}
Summary
All in all,梳理一遍Reference内部处理逻辑。
Stay Foolish
- 当
Reference处于Active时,第一个pending是怎么设置上去的? - 为何幽灵引用?
PhantomReference是如何产生的?