JAVA 如何合理且优雅的检测到对象被回收?

22 篇文章 0 订阅

如何合理的检测到对象被回收,然后进行操作。

有时我们需要在对象被回收之后触发一些操作,但是如何合理的监控对象回收呢?

Finalize函数

首先,大家肯定会很容易想到finalize() 函数,但是finalize()函数在java·中是不被推荐使用的。

finalize()的大致流程如下:

当对象变得GC Roots不可达的时候,GC 就会判断该对象是否覆盖了finalize()函数,如果没有覆盖,则直接进行回收;如果对象没有执行过finalize()方法,会将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize()方法。执行完之后,GC会再次判断该对象是否可达,如果不可达则会直接进行回收,否则,将不会对其进行回收,对象"复活"。

所以如果我们使用finalize()函数来进行监控对象回收并触发操作后存在以下两个问题:

  1. finalize()函数如果被长时间执行,那么需要回收的对象就得不到及时的回收;
  2. 如果在执行finalize()函数期间对象被重新引用了,那么对象就不会被回收了,所以就不符合我们的预期“检测到对象被回收,执行操作!”。

所以,还有其他的检测手段吗?当然,我们可以利用弱引用。

弱引用WeakReference

我们知道弱引用在垃圾回收时,只要具备垃圾回收的条件,那么就会回收它。

比如我们可以简单写出如下的代码,判断对象是否已经被回收。

				// 创建一个对象
        Object obj = new Object();
        // 创建弱引用
        WeakReference<Object> weakRef = new WeakReference<>(obj);
				// 手动触发垃圾回收
        System.gc();
        // 判断对象是否被回收
        if (weakRef.get() != null) {
            //对象尚未被回收
        } else {
            //对象已经被回收,处理后续的操作
        }

但是,上面的代码需要我们自己去判断去判断是否已经被回收,这样肯定是不太能满足业务要求,实际上大部分场景我们都需要被通知它被回收了。

如何做到?

其实在构建WeakReference的时候,可以传入一个引用队列ReferenceQueue

该队列的作用是,当GC时监测到该对象可回收的时候,就将它的弱引用放置到队列中。

所以,我们可以这么写

        // 创建引用队列
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        // 创建一个对象
        Object obj = new Object();
        // 创建弱引用,并关联到引用队列
        WeakReference<Object> weakRef = new WeakReference<>(obj, referenceQueue);
        // gc
        System.gc();
        // 检查引用队列中是否有对象被回收
        Reference<?> reference = referenceQueue.poll();
        if (reference != null) {
            // 对象已经被回收,处理后续的操作
        }

emmm,好像还是我们手动去检测的,其实Reference是一个阻塞队列,我们可以单独启动一个线程去调用其remove函数,它会阻塞,直到可以获取到值(也就是对象被回收),也就可以达到被通知的效果了。

如下:

        // 创建引用队列
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        // 创建一个对象
        Object obj = new Object();
        // 创建弱引用,并关联到引用队列
        WeakReference<Object> weakRef = new WeakReference<>(obj, referenceQueue);
				//单独开启线程监控,对象是否被回收
				registerClear(referenceQueue);


				//registerClear
				private static void registerClear(ReferenceQueue<StorageKV> queue) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                      	//线程被阻塞,直到对象被回收
                        if (queue.remove() != null) {
                            //对象已经被回收,处理后续的操作
                          	//这里面,不能引用到对象的方法属性等等,即不可以间接或者直接引用到需要被回收的对象,否则GC永远可达,就不会被垃圾回收了!!!
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

注意,重点:不要在开启的线程中直接或者间接的引用到需要被回收的对象,否则GC永远可达,无法进行垃圾回收。

🙆‍♀️。欢迎技术探讨噢!

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pumpkin的玄学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值