本文是Reference相关内容的最后一篇了。在这一篇,我们介绍一下 FinalReference 和 finalize方法。
finalize方法定义在Object中:
protected void finalize() throws Throwable { }在对象中我们可以重定义这个方法。在这个方法中可以释放各种资源。关于这一点,相信大多数人都比较熟悉了。但是关于 finalize骨嘴沙皮 到底什么时候被调用,好像没有人能说清楚。我们来看一下 hotspot 到底是怎么来实现的。
首先,hotspot 里会把类的构造方法的最后一条指令,也就是Java虚拟机指令的return,重写为一个特殊的指令:
void Rewriter::rewrite_Object_init(methodHandle method, TRAPS) { RawBytecodeStream bcs(method); while (!bcs.is_last_bytecode()) { Bytecodes::Code opcode = bcs.raw_next(); switch (opcode) { case Bytecodes::_return: *bcs.bcp() = Bytecodes::_return_register_finalizer; break;// 以下代码略这个指令,在解释器里是这样被执行的:
if (_desc-生物竞赛吧>bytecode() == Bytecodes::_return_register_finalizer) { assert(state回形针 == vtos, "only valid state"); __ movptr(c_rarg1, aaddress(0)); __ load_klass(rdi, c_rarg1); __ movl(rdi, Address(rdi, Klass::access_flags_offset())); __ testl(rdi, JVM_ACC_HAS_FINALIZER); Label skip_register_finalizer; __ jcc(Assembler::zero, skip_register_finalizer); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::regi贯通筋ster_finalizer), c_rarg1); __ bind(skip_register_finalizer); }这其实是一段手写的汇编,大概是这样的,Klass结构的指针先加载到rdi寄存器,然后,去取得它的acess_flag,去检查这个Klass是否定义了finalize方法。如果一个类里定义了finalize,那么在加载的时候,就会使它的access_flag中的JVM_ACC_HAS_FINALIZER置位。
也就是说,如果这个Klass中定义了finalize方法,就会调用 register_finalizer 这个方法。这个方法我们就不去看了,有兴趣的同学自己去查。
反正最终呢,会调用到Finalizer.java中定义的这个类中:
/* Invoked by VM */ static void register(Object finalizee) { new Finalizer(finalizee); }就是说,如果kosher认证一个定义了finalize方法的类在初始化的时候,就会调用一下这个static方法,生成一个Finalizer对象,而我们自己的对象就是这个方法中所使用的finalizee。
也就是说,我们新建一个带 finalize 方法的对象,就会伴生一个 Finalizer 对象。我们看一下,这个类的定义:
final class Finalizer extends FinalReference<Object> { /* Package-private; must be in same package as the Reference class */ private static ReferenceQueue<Object> queue = new ReferenceQueue<>(); private static F电动车头盔inalizer unfinalized = null; private static final Object lock = new Object(); private Finalizer next = null, prev = null; private boolean hasBeenFinalized() { return (next 阿图岛== this); } private void add() { synchronized (lock) { if (unfinalized != null) { this.next = unfinalized; unfinalized.prev = this; } unfinalized = this; } } private void remove() { synchronized (lock) { if (unfinalized == this) { if (this.next != null) { unfinalized = this.next; } else { unfinalized = this.prev; } } if (this.next != null) { this.next.prev = th水龙呤is.prev; } if (this.prev != null) { this.prev.next = this.next; } 寿险规划师 this.next = this; /* 反函数的定义Indicates that this has been finalized */ this.prev = this; } } private Finalizer(Object finalizee) { super(finalizee, queue); add(); }Hoho,绕了半天,这和上节课所讲的Cleaner何其相似,都是一个双向队列,在构造的时候就把新建的Finalizer对象加入到一个双向链表中。
注意上面这段代码里的queue,大家还记得Reference Handler线程吗?那个线程会把所有的Reference从pending链表上取出来,然后平凡的天才加入到一个queue中。对于FinalReference,就都会加入到上述代码中的queue中。这个queue中的所有对象,都会被一个名为Finalizer的线程所处理。我们之前也介绍过Reference Handle中国智力运动网r是2号线程,Finalizer线程是3号线程。Finalizer线程的代码如下所示:
private static class FinalizerThread extends Thread { private volatile boolean running; FinalizacomeerThread(ThreadGroup g) { super(g, "Finalizer"); } public void run() { if (running) return; // Finalizer thread starts before System.initializeSystemClass // is called. Wait until JavaLangAccess is现在学什么技术有前途 available while (!VM.isBooted()) { // delay until VM completes initialization try { VM.awaitBooted(); } catch (InterruptedException x) { // ignore and continue } } final JavaLangAccess jla = SharedSecrets.ge维纳滤波tJavaLangAccess(); running = true; for (;;) { try { Finalizer f = (Finalizer)queue.remove(); f.runFinalizer(jla); } catch (InterruptedException x) { // ignore and continue } } } }最终就是会从队列中不断地取出Finalizer对象,然后去调用它的runFinalizer方法。
private void runFinalizer(JavaLangAccess jla) { synchronized (this) { if (hasBeenFinalized()) return; remove(); } try { Object finalizee 蛛网膜= this.get(); if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { jla.invokeFinalize(finalizee); /* Clear stack slot containing this variable, to decrease the chances of false retention with a conservative GC */ finalizee = null; } } catch南宁植发 (Throwable x) { } super.clear(); }这个方法使用了JavaLangAccess做为参数,其实就是一次虚函数调用,调用到finalizee中的 finalize 方法。
hasBeenFinalized的作用就是保证finalize方法只会被调用一次。
Finalizer vs. Cleaner因为Finalizer也是一种Reference外贸整合营销,所以前边Reference的处理逻辑是和Weak, Soft reference的逻辑十分相似的。
而且Finalizer和Cleaner的作用也十分相似,但有一个巨大的不同在于,finalize方法里可以使object 复活,而 Cleaner 的 clean 方法中不能使得对象复活。
这是因为 finalize 中,可以通过 this 市场与政府指针访问到 object 对象,例如:
public void finalize() { Other.ref = this;}这样的话,一个本来应该被回收的对象又在finalize之后复活了。但是Cleaner为什么不行呢?因为它的基类是一个PhantomReference,这个“鬼引用”的 get 方法是这样的:
public class PhantomReference<T> extends Reference<T> { public T get() { 脑子 return null; } // 其它代码略}永远返回null,也就是说对于Cleaner,创建了以后,就再也不能访问它的referent了。
所以现在有人提案,在Java中去掉finalize方法,只使用Cleaner来维护。
其实,这里面还有其他的一些细节,比如为什么Weak的referent会被JVM清掉,而Finalizer又不会清掉自己的referent呢。这些就都不重要了。我就不再讲解了。
我想,经过了这四节课,我们就可以把WeakReference, SoftReference, PhantomReference和Cleaner, FinalReference和Finalizer这6种对象全部搞清楚了。
上一节课:PhantomReference & Cleaner
下一节课:Tracing GC(1)
课程目录:课程目录
本文发布于:2023-06-04 07:18:47,感谢您对本站的认可!
本文链接:http://www.ranqi119.com/ge/85/214384.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |