我的原则:先会用再说,内部慢慢来。
学以致用,根据场景学源码
一、前言
建议提前阅读:
- 【JAVA Reference】ReferenceQueue 与 Reference 源码剖析(二)
- 【JAVA Reference】Cleaner 源码剖析(三)
- 【JAVA Reference】Cleaner 对比 finalize 对比 AutoCloseable(四)
二、架构
2.1 代码架构
- Finalizer 继承 FinalReference 再继承 Reference。
2.2 UML流程图
三、思考 Finalizer Vs Cleaner 放一起
- 我们知道重写 finalize 方法与定义一个 cleaner 都可以实现在gc回收前进行一系列操作。建议先看 【JAVA Reference】Cleaner 对比 finalize 对比 AutoCloseable(四)
3.1 代码 demo
public class _05_03_TestCleanerWithFinalize {
public static void main(String[] args) throws Exception {
int index = 0;
while (true) {
Thread.sleep(1000);
// 提醒 GC 去进行垃圾收集了
System.gc();
// 该对象不断重新指向其他地方,那么原先指针指向的对象的就属于需要回收的数据
DemoObject obj = new DemoObject("demo" + index++);
Cleaner.create(obj, new CleanerTask("thread_" + index++));
}
}
@Data
@AllArgsConstructor
@ToString
static class DemoObject {
private String name;
@Override
protected void finalize() throws Throwable {
System.out.println("finalize running DoSomething ..." + name);
}
}
static class CleanerTask implements Runnable {
private String name;
public CleanerTask(String name) {
this.name = name;
}
// do something before gc
@Override
public void run() {
System.out.println("CleanerTask running DoSomething ..." + name );
}
}
}
输出:
finalize running DoSomething ...demo0
CleanerTask running DoSomething ...thread_1
finalize running DoSomething ...demo2
CleanerTask running DoSomething ...thread_3
finalize running DoSomething ...demo4
CleanerTask running DoSomething ...thread_5
finalize running DoSomething ...demo6
CleanerTask running DoSomething ...thread_7
finalize running DoSomething ...demo8
...
可以看到,每次 finalize 总是比 Cleaner 先执行,不管你run几次,结果都一样,那么思考一下为什么?
3.2 为什么 finalize 总是比 Cleaner 先执行 ?
- 结论先抛出来: Cleaner 和 finalize 内部都有指针Pointer 指向了即将要回收的 Object 对象,但是 Cleaner 底层是虚引用(PhamtonReference),而 finalize的底层是 Finalizer ,属于强引用。所以,必须强引用的释放完对象,才轮到 Cleaner。
四、Finalizer 源码剖析
4.1 父类 FinalReference
class FinalReference<T> extends Reference<T> {
public FinalReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
- 注意:这个类的修饰符是 default ,也就是只能在包 java.lang.ref 内被使用 。
- 由继承关系可以得知,FinalReference 只有一个子类 Finalizer
4.2 Finalizer 类
final class Finalizer extends FinalReference<Object>
- 注意,Finalizer 是个 final 类,也就是断子绝孙类,不会有继承,可以防止篡改与注入。
- Finalizer 类的修饰符是 default ,也就是只能在包 java.lang.ref 内被使用 。那么总览了一下,他是 JVM 调用的。
4.3 Finalizer 类变量
4.3.1 私有static变量 queue
Finalizer引用的对象被gc之前,jvm会把相应的Finalizer对象放入队列 queue
private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
=== 关于Reference的四个状态,可以看图 ===
=== 点击查看top目录 ===
4.3.2 私有static变量 unfinalized
静态的Finalizer对象链,每=== 实例化 ===一个对象,这个队列就会插入=== add ===一个。
// 静态的Finalizer对象链
private static Finalizer unfinalized = null;
4.3.3 next + prev 指针
// 双端指针
private Finalizer
next = null,
prev = null;
4.3.3 私有 static final 变量 lock
private static final Object lock =