首页 > 生活

finalize方法

更新时间:2025-05-21 08:21:09 阅读: 评论:0

本文是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小时内删除。

标签:方法   finalize
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 站长QQ:55-9-10-26|友情:优美诗词|电脑我帮您|扬州装修|369文学|学编程|软件玩家|水木编程|编程频道