ReferenceHandler
是Reference
的内部类 , 继承了Thread
, 在run方法中开启了一个循环,不断的执行tryHandlePending
方法,处理Reference中
的pending
列表:
public abstract class Reference<T> {private static class ReferenceHandler extends Thread {// ...ReferenceHandler(ThreadGroup g, String name) {super(g, name);}public void run() {while (true) {// 处理pending列表tryHandlePending(true);}}} }
Cleaner会启动一个优先级最高的守护线程,不断调用tryHandlePending来检测是否有需要回收的引用对象(还未进行真正的回收),然后进行处理 。
处理pending列表垃圾回收器会将要回收的引用对象放在Reference
的pending
变量中,从数据类型上可以看出pending
只是一个Reference
类型的对象,并不是一个list , 如果有多个需要回收的对象,如何将它们全部放入pending
对象中?
可以把pengding看做是一个链表的头结点,假如有引用对象被判定需要回收,如果pengding为空直接放入即可,如果不为空,将使用头插法将新的对象加入到链表中,也就是将新对象的discovered指向pending对象 , 然后将pending指向当前要回收的这个对象 , 这样就形成了一个链表,pending指向链表的头结点 。
文章插图
在pending链表中的引用对象处于pending状态 。
接下来看
tryHandlePending
方法的处理逻辑:- 如果pending不为空,表示有需要回收的对象,此时将pengding指向的对象放在临时变量
r
中,并判断是否是Cleaner类型 , 如果是将其强制转为Cleaner
,记录在临时变量c
中,接着更新pending的值为r的discovered,因为discovered中记录了下一个需要被回收的对象,pengding需要指向下一个需要被回收的对象;
pending如果为NULL,会进入到else的处理逻辑,返回值为参数传入的waitForNotify的值 。
- 判断
Cleaner
对象是否为空 , 如果不为空,调用Cleaner的clean方法进行清理;
- 获取引用对象关联的引用队列 , 然后调用
enqueue
方法将引用对象加入到引用队列中;
- 返回true;
public abstract class Reference<T> {// 指向pending列表中的下一个节点transient private Reference<T> discovered;// 静态变量pending列表 , 可以看做是一个链表,pending指向链表的头结点private static Reference<Object> pending = null;static boolean tryHandlePending(boolean waitForNotify) {Reference<Object> r;Cleaner c;try {synchronized (lock) {// 如果pending不为空if (pending != null) {// 获取pending执行的对象r = pending;// 如果是Cleaner类型c = r instanceof Cleaner ? (Cleaner) r : null;// 将pending指向下一个节点pending = r.discovered;// 将discovered置为空r.discovered = null;} else {// 等待if (waitForNotify) {lock.wait();}return waitForNotify;}}} catch (OutOfMemoryError x) {Thread.yield();// retryreturn true;} catch (InterruptedException x) {// retryreturn true;}if (c != null) {// 调用clean方法进行清理c.clean();return true;}// 获取引用队列ReferenceQueue<? super Object> q = r.queue;// 如果队列不为空 , 将对象加入到引用队列中if (q != ReferenceQueue.NULL) q.enqueue(r);// 返回truereturn true;}}
释放内存在Cleaner
的clean方法中,可以看到,调用了thunk的run方法,前面内容可知 , thunk指向的是Deallocator对象,所以会执行Deallocator的run方法,Deallocator的run方法前面也已经看过,里面会对DirectByteBuffer的堆外内存进行释放:public class Cleaner extends PhantomReference<Object> {public void clean() {if (!remove(this))return;try {// 调用run方法thunk.run();} catch (final Throwable x) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {if (System.err != null)new Error("Cleaner terminated abnormally", x).printStackTrace();System.exit(1);return null;}});}}}
总结Cleaner是一个虚引用,它实际引用的对象DirectByteBuffer如果被GC判定为需要回收,会将引用该对象的Cleaner加入到pending列表,ReferenceHandler线程会不断检测pending是否为空 , 如果不为空,就对其进行处理:
- 如果对象类型为Cleaner,就调用Cleaner的clean方法进行清理,Cleaner的clean方法又会调用Deallocator的run方法,里面调用了freeMemory方法对DirectByteBuffer分配的堆外内存进行释放;
- 将Cleaner对象加入到与其关联的引用队列中;
推荐阅读
- 怎么删除微信好友(一键恢复已删除好友)
- 浅谈-动态路由之OSPF的理解
- Jupyter,Matplotlib,Pandas 【机器学习】利用 Python 进行数据分析的环境配置 Windows
- 奇迹暖暖幽夜魅影怎么搭配
- 摩尔庄园10月28日神奇密码是多少
- 荣耀magic3支持鸿蒙系统吗_荣耀magic3能升级鸿蒙吗
- 可持久化数组 P3919 【模板】可持久化线段树 1
- 斗罗大陆武魂觉醒荒野行纪2
- 祖玛阁5去6怎么走(祖玛阁杂货店到6层怎么走)
- 【nginx】使用 nginx 时,使用 sub_filter 注入 js 代码,例如 google analysis 等