通过ReferenceQueue来监听被GC

通过ReferenceQueue来监听被GC

实例代码

class ReferenceQueueActivity : Activity() {

    private val queue = ReferenceQueue<Person?>()
    private var mWeakReference: WeakReference<Person?>? = null

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var s: Person? = Person()
        mWeakReference = WeakReference(s, queue)
        s = null
        System.gc()

        val button = Button(this)
        button.text = "poll"
        button.setOnClickListener {
            System.gc()
            println("mWeakReference.get()" + mWeakReference!!.get())
            val reference = queue.poll()
            if (reference != null) {
                println("reference" + reference.get())
                if (mWeakReference === reference) {
                    println("哈哈监听到啦!")
                }
            }
        }
        setContentView(button)
    }
}

先创建一个Person然后把它赋值为空,点击button会发现如下log


com.seekting.demo2018 I/System.out: mWeakReference.get()null
com.seekting.demo2018 I/System.out: referencenull
com.seekting.demo2018 I/System.out: 哈哈监听到啦!
com.seekting.demo2018 I/System.out: mWeakReference.get()null

会发现poll出来一个WeakReference和mWeakReference相等,而此时mWeakReference里的引用已经为空了

通过这个能监听到一个对象被释放。

源码解析

ReferenceQueue源码

ReferenceQueue源码不多,强烈建议看

首先看成员变量:

static ReferenceQueue<Object> NULL = new Null<>();
static ReferenceQueue<Object> ENQUEUED = new Null<>();

static private class Lock { };
private Lock lock = new Lock();
private volatile Reference<? extends T> head = null;
private long queueLength = 0;

lock:synchronized用到的一个锁
head:指向链表头部的一个引用(它是后进先出的结构,有点像栈~)
queueLength:链表长度

重点讲讲NULL和ENQUEUED(enqueue()操作)

Reference类里有一个成员

  volatile ReferenceQueue<? super T> queue;

而这个成员在ReferenceQueue里多被灵活应用:
1. 当你通过WeakReference(s, queue)构造时
它表示,如果WeakReference是入队,当然是找指定的ReferenceQueue,这里就是queue对象
2. 当你通过当你通过WeakReference(s)构造时
它表示,不需要入队,也就是说内存被回收时不会入队

Reference(T referent, ReferenceQueue<? super T> queue) {
       this.referent = referent;
       this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
   }

Reference的构造告诉我们它会用ReferenceQueue.NULL;而Null也是ReferenceQueue的子类

它的实现就是返回false,啥也不做,就等于内存被回收时不入队,也就是不用监听内存回收

private static class Null<S> extends ReferenceQueue<S> {
     boolean enqueue(Reference<? extends S> r) {
         return false;
     }
 }
  1. 相比1情况,如果传的是一个ReferenceQueue,那它会走真实的入队操作

    boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
           synchronized (lock) {
               ReferenceQueue<?> queue = r.queue;
               //如果该Reference的queue是空就返回false
               //如果是ENQUEUED就返回false
               if ((queue == NULL) || (queue == ENQUEUED)) {
                   return false;
               }
               assert queue == this;//断言这里的queue是自己
               r.queue = ENQUEUED;//这里表示它已经入队了,通过ENQUEUED来防止它再入队
               r.next = (head == null) ? r : head;//r->head->1->2
               head = r;//head->0->1->2
               queueLength++;//队列长度+1
               if (r instanceof FinalReference) {
                   sun.misc.VM.addFinalRefCount(1);
               }
               lock.notifyAll();
               return true;
           }
       }
    

    入队代码很容易读懂:

    1. 如果Reference的queue是Null或是ENQUEUED就返回false不操作

    2. 断言这里的queue是自己

    3. 新加的节点是会变成头节点,有点像栈的结构

出队操作poll()

public Reference<? extends T> poll() {
    if (head == null)
        return null;
    synchronized (lock) {
        return reallyPoll();
    }
}

private Reference<? extends T> reallyPoll() {
    Reference<? extends T> r = head;//拿到头节点
    if (r != null) {
        head = (r.next == r) ?
            null ://头节点的下一个是自己就说明是最后一个节点了,拿走了,head当然为空了
            r.next;//否则头节点就是以前的头节点的下一个节点:后进先出
        r.queue = NULL;//把它的queue置为空,也就是再也没有机会入队了
        r.next = r;//把该节点的下一个节点引用截断
        queueLength--;//长度-1
        if (r instanceof FinalReference) {
            sun.misc.VM.addFinalRefCount(-1);
        }
        return r;
    }
    return null;
}

注意出队入队的时候都加了synchronized同步

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个使用ReferenceQueue的示例代码: ``` import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; public class ReferenceQueueDemo { public static void main(String[] args) { Object obj = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue); // 检查弱引用是否被加入到了ReferenceQueue中 new Thread(() -> { while (true) { Reference<?> ref = referenceQueue.poll(); if (ref != null) { System.out.println("Weak reference is garbage collected: " + ref); } } }).start(); // 立即清除强引用 obj = null; System.gc(); // 等待一段时间以便垃圾回收完成 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } ``` 在这个示例中,我们创建了一个Object对象,并将其封装在一个WeakReference中。我们同时创建了一个ReferenceQueue对象,用于存放被垃圾回收的引用对象。在一个单独的线程中,我们不断地检查ReferenceQueue中是否有弱引用对象被回收了。 接着,我们立即将强引用obj设为null,并手动调用System.gc()方法,以便触发垃圾回收。最后,我们等待一段时间以便垃圾回收完成。 在这个示例中,我们通过使用ReferenceQueue来监控弱引用对象是否被回收,从而及时进行必要的清理工作。如果垃圾回收器回收了弱引用对象,那么该对象将会被加入到ReferenceQueue中,并且我们可以在另一个线程中对其进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值