Cleaner
从名字上可以看出与清理有关,Bits
的reserveMemory
方法底层也是通过Cleaner
来进行清理,所以Cleaner是重点关注的类 。
Deallocator
是DirectByteBuffer
的一个内部类,并且实现了Runnable接口,在run方法中可以看到对内存进行了释放,接下来就去看下在哪里触发Deallocator
任务的执行:
class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {private static class Deallocator implements Runnable {// ...private Deallocator(long address, long size, int capacity) {assert (address != 0);this.address = address; // 设置内存地址this.size = size;this.capacity = capacity;}public void run() {if (address == 0) {// Paranoiareturn;}// 释放内存unsafe.freeMemory(address);address = 0;Bits.unreserveMemory(size, capacity);}}}
CleanerCleaner
继承了PhantomReference
,PhantomReference
是Reference
的子类 , 所以Cleaner
是一个虚引用对象 。
文章插图
创建Cleaner虚引用需要与引用队列结合使用,所以在Cleaner中可以看到有一个
ReferenceQueue
, 它是一个静态的变量 , 所以创建的所有Cleaner对象都会共同使用这个引用队列 。在创建Cleaner的
create
方法中 , 处理逻辑如下:- 通过构造函数创建了一个Cleaner对象 , 构造函数中的referent参数为
DirectByteBuffer
, thunk参数为Deallocator
对象,在构造函数中又调用了父类的构造函数完成实例化; - 调用add方法将创建的Cleaner对象加入到链表中 , 添加到链表的时候使用的是头插法,新加入的节点放在链表的头部,first成员变量是一个静态变量,它指向链表的头结点,创建的Cleaner都会加入到这个链表中;
public class Cleaner extends PhantomReference<Object>{// ReferenceQueue队列private static final ReferenceQueue<Object> dummyQueue = new ReferenceQueue<>();// 静态变量,链表的头结点,创建的Cleaner都会加入到这个链表中static private Cleaner first = null;// thunkprivate final Runnable thunk;public static Cleaner create(Object ob, Runnable thunk) {if (thunk == null)return null;// 创建一个Cleaner并加入链表return add(new Cleaner(ob, thunk));}private Cleaner(Object referent, Runnable thunk) {super(referent, dummyQueue); // 调用父类构造函数 , 传入引用对象和引用队列this.thunk = thunk; // thunk指向传入的Deallocator}private static synchronized Cleaner add(Cleaner cl) {// 如果头结点不为空if (first != null) {// 将新加入的节点作为头结点cl.next = first;first.prev = cl;}first = cl;return cl;}}
Cleaner调用父类构造函数时,最终会进入到父类Reference
中的构造函数中:referent:指向实际的引用对象,上面创建的是
DirectByteBuffer
,所以这里指向的是DirectByteBuffer
。queue:引用队列,指向
Cleaner
中的引用队列dummyQueue
。public class PhantomReference<T> extends Reference<T> {// ...public PhantomReference(T referent, ReferenceQueue<? super T> q) {super(referent, q); // 调用父类构造函数}}public abstract class Reference<T> {/* 引用对象 */private T referent;// 引用队列volatile ReferenceQueue<? super T> queue;Reference(T referent, ReferenceQueue<? super T> queue) {this.referent = referent;// 设置引用队列this.queue = (queue == null) ? ReferenceQueue.NULL : queue;}}
文章插图
启动ReferenceHandler线程
Reference
中有一个静态方法 , 里面创建了一个ReferenceHandler
并设置为守护线程,然后启动了该线程 , 并创建了JavaLangRefAccess
对象设置到SharedSecrets
中:public abstract class Reference<T> {static {ThreadGroup tg = Thread.currentThread().getThreadGroup();for (ThreadGroup tgn = tg;tgn != null;tg = tgn, tgn = tg.getParent());// 创建ReferenceHandlerThread handler = new ReferenceHandler(tg, "Reference Handler");// 设置优先级为最高handler.setPriority(Thread.MAX_PRIORITY);handler.setDaemon(true);handler.start();// 这里设置了JavaLangRefAccessSharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {@Overridepublic boolean tryHandlePendingReference() {// 调用了tryHandlePendingreturn tryHandlePending(false);}});}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 怎么删除微信好友(一键恢复已删除好友)
- 浅谈-动态路由之OSPF的理解
- Jupyter,Matplotlib,Pandas 【机器学习】利用 Python 进行数据分析的环境配置 Windows
- 奇迹暖暖幽夜魅影怎么搭配
- 摩尔庄园10月28日神奇密码是多少
- 荣耀magic3支持鸿蒙系统吗_荣耀magic3能升级鸿蒙吗
- 可持久化数组 P3919 【模板】可持久化线段树 1
- 斗罗大陆武魂觉醒荒野行纪2
- 祖玛阁5去6怎么走(祖玛阁杂货店到6层怎么走)
- 【nginx】使用 nginx 时,使用 sub_filter 注入 js 代码,例如 google analysis 等