如何合理的检测到对象被回收,然后进行操作。
有时我们需要在对象被回收之后触发一些操作,但是如何合理的监控对象回收呢?
Finalize函数
首先,大家肯定会很容易想到finalize()
函数,但是finalize()
函数在java
·中是不被推荐使用的。
finalize()
的大致流程如下:
当对象变得GC Roots
不可达的时候,GC
就会判断该对象是否覆盖了finalize()
函数,如果没有覆盖,则直接进行回收;如果对象没有执行过finalize()
方法,会将其放入F-Queue
队列,由一低优先级线程执行该队列中对象的finalize()
方法。执行完之后,GC
会再次判断该对象是否可达,如果不可达则会直接进行回收,否则,将不会对其进行回收,对象"复活"。
所以如果我们使用finalize()
函数来进行监控对象回收并触发操作后存在以下两个问题:
finalize()
函数如果被长时间执行,那么需要回收的对象就得不到及时的回收;- 如果在执行
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永远可达,无法进行垃圾回收。
🙆♀️。欢迎技术探讨噢!