FinalReference
类的功能、jvm
中finalize()
方法的实现方式、为什么实现finalize()
方法的类要至少经过两次gc
才能回收。
FinalReference
的子类是Finalizer
,FinalReference
类的权限是default
的,Finalizer
类的权限是default
的,并且有final
修饰符,这两个类原则上不让开发者使用。他们和jvm
配合实现java
中对象实例被gc
回收之前执行finalize()
方法。java
中的类有构造方法,不像C++
中有析构函数,finalize()
某种意义上是“析构函数”。
接下来看一下java
是怎么实现finalize()
方法的。
首先需要解释一个概念,finalizee
类,finalize()
方法在超类Object
中的原型是
protected void finalize() throws Throwable { }
如果子类复写了这个方法,既这个方法体不为空,这种类成为finalizee
类,生成的实例时finalizee
实例。这种实例jvm
会比其他普通对象的实例多几个不同的处理步骤。
从FinalReference
类中的结构来看。当一个finalizee
对象被创建时,jvm
会以当前对象为参数调用static void register(Object finalizee)
方法。该方法封装成Finalizer
实例并放置到ufinalized
链表上。unfinalized
是静态成员属性,在jvm
实例中是唯一的。
final class Finalizer extends FinalReference {
private static ReferenceQueue queue = new ReferenceQueue();
private static Finalizer unfinalized = null; // finalizee对象对应的Finalizer链表,jvm中全局的
private static final Object lock = new Object();
private Finalizer next = null,prev = null;
private void add()
private void remove()
private Finalizer(Object finalizee) {
super(finalizee, queue);
add();
}
static void register(Object finalizee) {
new Finalizer(finalizee);
}
private void runFinalizer(JavaLangAccess jla)
private static void forkSecondaryFinalizer(final Runnable proc)
static void runFinalization()
static void runAllFinalizers()
}
另一方面,在静态代码块中会有一个优先级较低的守护线程,该守护线程从queue
中取出Finalizer
,调用runFinalizer()
,runFinalizer()
运行该finalizee
对象的finalize()
方法。runFinalizer()
中限制只会运行一次finalize()
方法。同时把当前finalizee
对应的Finalizer
从unfinalized
链表中移除。是什么时候向queue
中放置的Finalizer
呢?gc
判定当前的finalizee
对象需要回收的时候,会把finalizee
对应的Finalizer
放到queue
中,但此时该finalizee
还未被回收。这些逻辑是Reference
类中的逻辑(可以参看文章中“引用的内部逻辑”部分)。
当运行完finalizee
对象的finalize()
方法后,对应的Finalizer
从unfinalized
链表中移除、Finalizer
中的referent
置为null
,finalizee
对象变成孤立的对象。如果再次发生gc
,finalizee
对象会被回收,Finalizer
会被回收。因此一个finalizee
对象会至少经过两次的gc
才能被真正回收。
————————————————
版权声明:本文为CSDN博主「玩转生活」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/csdn_app/article/details/103994271