JAVA引用(WeakHashMap、Cleaner)

Expunge 删除,抹去
stale   陈腐、老旧的

Reference 引用类

强引用、软引用、弱引用、虚引用

软引用、弱引用、虚引用,可以配合ReferenceQueue实现对象被回收时候的监听

Reference

重要属性:

1. private T referent;       
weakHashMap中就是那个Entry的Key   Treated specially by GC

2. volatile ReferenceQueue<? super T> queue;
Reference队列,GC后Reference对象会被加到这里面,是放到队列头部。

3. private static Lock lock = new Lock();
GC时候必须要获得这个锁

4. private static Reference<Object> pending = null;
跟虚拟机,GC打交道的。 GC后Reference对象会被GC自动设置到这个引用中,然后由ReferenceHandler线程把他放到ReferenceQueue里面.
    
5. transient private Reference<T> discovered; 
跟虚拟机,GC打交道的  used by VM 

6. volatile Reference next;
ReferenceQueue是linkedList类似是实现,链表结构,Reference就相当于其中的Node,有一个next的指针指向下一个对象。

ReferenceHandler 内部类:

  1. 在Reference类的静态代码块中初始化,然后运行。
  2. 主要功能是在tryHandlePending()方法中,把Reference类的静态变量pending指向的Reference对象丢到他自己的queue中。
  3. 于此同时,如果pending所指的Reference对象是Cleaner,那么还会执行Cleaner的clean方法,而且不会放到queue中,执行了clean方法后就返回了。

Cleaner在DirectByteBuffer中有使用,下面介绍.

Cleaner类

public class Cleaner extends PhantomReference<Object> 集成虚引用, 也就是说cleaner的var0只要他强引用消失了,那么var0所指的对象就随时会被GC掉。

//创建方法,add方法也是把cleaner自身构建成一个链表结构
public static Cleaner create(Object var0, Runnable var1) {
   return var1 == null ? null : add(new Cleaner(var0, var1));
}
private Cleaner(Object var1, Runnable var2) {
   super(var1, dummyQueue);
   this.thunk = var2;
}

clean方法,在Reference的tryHandlePending方法中会执行,即Cleaner关联的对象被GC放到pending中,然后ReferenceHandler线程执行tryHandlePending时候执行。

    public void clean() {
        //删除自身节点
        if (remove(this)) {
            try {
            //执行创建时候传进来的runnable对象的run方法,执行相关的业务逻辑
            //比如DirectByteBuffer类中,他有个cleaner,run方法是用于释放直接内存
            //详见下
                this.thunk.run();
            } catch (final Throwable var2) {
                //省略。。。
            }
        }
    }

Cleaner在DirectByteBuffer中有使用,下面介绍.

DirectByteBuffer中Cleaner的使用

DirectByteBuffer基于unsafe类的allocateMemory来分配直接内存使用。

基于Cleaner来进行直接内存的释放。

直接内存申请,释放流程:
  1. new DirectByteBuffer(1024)构建对象,构造方法内部调用Unsafe.allocateMemory申请到直接内存(不归JVM GC管辖)
  2. 构造方法内部还调用``Cleaner.create(this, new Deallocator(base, size, cap))`把当前DirectByteBuffer设置为虚引用,并设置一个Deallocator的runnable类。
  3. 当DirectByteBuffer强引用消失,即不可达之后,由于Cleaner是虚引用,不影响GC,所以DirectByteBuffer被GC掉了
  4. 与此同时,Cleaner对象也被GC设置到Reference的静态属性pending中(这个时候Cleaner即Reference的Referent引用指向的DirectByteBuffer已经是null了)
  5. Reference中的ReferenceHandler线程执行tryHandlePending,处理pending,即该Cleaner
  6. tryHandlePending中调用Cleaner的clean方法,clean方法内部调用构建时候传入的runnable对象的run方法,即Deallocator的run方法。
  7. Deallocator的run方法中调用了unsafe.freeMemory(address);来释放DirectByteBuffer在构建时候申请的直接内存
  8. 以此来完成直接内存的释放

WeakHashMap

https://hongjiang.info/java-referencequeue/

以前设计缓存时也曾过用WeakHashMap来实现,对Java的Reference稍做过一些了解,其实这个问题,归根到底,是个Java GC的问题,由垃圾回收器与ReferenceQueue的交互方式决定的。WeakHashMap的实现也是通过ReferenceQueue这个“监听器”来优雅的实现自动删除那些引用不可达的key的。

Entry类继承了WeakReference, 其中的Key是弱引用,并且WeakHashMap中定义了一个ReferenceQueue,用于监听key的回收

  //Entry类继承弱引用
  private static class Entry<K,V> extends WeakReference<Object> {}
  
  //WeakHashMap中的实例变量用于监听key的回收
  private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
流程
  1. 当WeakHashMap中Key没有其他强引用时候,发生了一次任意GC,由于Entry的Key是弱引用,那么被GC回收。

  2. 被GC回收的同时,Entry类(继承了弱引用)会由GC自动放入到Reference的静态属性pending中。

  3. Reference类在类初始化后有个static代码块,里面启动了一个高优先级的daemon线程,用于把pending指向的Reference对象放到ReferenceQueue中

  4. 经过3后,Entry就被丢到了ReferenceQueue中

  5. 当你在GC后调用WeakHashMap的get、put、size等方法时候,他会调用自己的expungeStaleEntries()方法。

  6. expungeStaleEntries方法会poll出queue中的Entry类,然后把entry类处理下,value=null,释放掉value的强引用,然后处理前后的指针,size--之类。

  7. 经过6之后,key对应的value强引用也被抹去了,所以GC也可以把value以及整个entry回收掉。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342