详解java中finalize的实现与相应的执行过程

 更新时间:2016年09月11日 08:57:20   作者:i flym  
在常规的java书籍中,即会描述 object的finalize方法是用于一些特殊的对象在回收之前再做一些扫尾的工作,但是并没有说明此是如何实现的.本篇从java的角度(不涉及jvm以及c++),有需要的朋友们可以参考借鉴。

FinalReference引用

此类是一个package类型,表示它并不是公开的一部分,继承自Reference, 即表示也是一种特定的引用类型,因此每个包装在其中的对象在被回收之前,自己都会放到指定的referqyebceQueue当中.

这个引用对象专门为带finalize方法的类服务,可以理解为每一个有相应的方法的对象,其都会封装为一种finalRefernece对象.

因为finalize方法是object定义的,其默认实现为空.那么如果重写了此方法,那么方法体肯定不为空.即可以通过这一种区别来.只要finalize方法实现不为空的类,此产生的对象都需要被注册到finalRefernece中.

这一步可以通过在newInstance的时候,即调用object默认构造方法的时候,就可以进行相应的注册了.

Finalizer#register方法

主要调用了此方法,就会产生相应的finalizer对象,而finalizer对象是继承于finalReference的.此方法声明如下:

/* Invoked by VM */
static void register(Object finalizee) {
 new Finalizer(finalizee);
}

从上面注释可以看出,此方法会被jvm在特定时期调用.
然后切换到Finalizer的构造方法,如下所示:

private Finalizer(Object finalizee) {
 super(finalizee, queue);
 add();
}

可以看出,相应的引用对象会通过queue进行回调.add的作用在于将所有还未进行finalize方法的对象存起来,在最后System.shutdown时调用.通过Runtime#runFinalizersOnExit进行设置.

ReferenceQueue

此引用队列会在相应reference对象的内部对象被回收之前放到此队列中(详细说明在另一篇关于reference中再说明.),因为只需要从此队列中拿到相应的对象,那么此对象就肯定是准备被回收的.

那么在回收之前调用相应的finalize方法即可.

FinalizerThread线程

此线程即是从queue里面,不停的获取数据,然后调用相应的finalize方法.相应的代码如下所示:

for (;;) {
 try {
  Finalizer f = (Finalizer)queue.remove();
  f.runFinalizer(jla);
 } catch (InterruptedException x) {
  // ignore and continue
 }
}

而相应的runFinalizer如下所示:

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();

在上面的逻辑当中,首先调用remove将其从未finalize中移除.这个方法是保证每个对象的finalize最多只会被调用一次,即当前这次调用完了.它就会被记相应的状态,即hasBeenFinalized返回为true(其实就是把里面的next指针指向自己.即自己从未finalize中移除,同时也不需要再次调用finalize了).

接下来就是调用相应的finalize方法,上面的jla.invokeFinalize其实就是调用相应对象的finalize方法. 在这个处理中,首先通过get获取原始对象.在整个jvm处理中,针对finalizeReference在回收之前默认是不将引用设置为null.因为这里,总是能够获取相应的引用对象.

处理完之后,最后调用相应的clear,清除相应的引用.这样达到最终引用没有其它对象可引用的效果.

在上面的处理当中,并没有限定调用finalize的时间.因此,一旦如果某个对象的finalize调用慢,就会影响到整个回收链的执行,这下就会产生相应的OOM异常了.因此,除非特殊情况,就不要重写finalize,相应的场景都应该有其它方法可以处理.比如guava中的FinalizableReference.

finalizer启动线程

在上面的线程,在相应的进程启动过程中就会被启动.可以理解为,对象通过调用register(object)触发finalizer类的初始化.然后,在静态初始化块当中,就会启动相应的回收线程.相应的初始化代码如下所示:

static {
 ThreadGroup tg = Thread.currentThread().getThreadGroup();
 for (ThreadGroup tgn = tg;
   tgn != null;
   tg = tgn, tgn = tg.getParent());
 Thread finalizer = new FinalizerThread(tg);
 finalizer.setPriority(Thread.MAX_PRIORITY - 2);
 finalizer.setDaemon(true);
 finalizer.start();
}

上面的static是静态初始化块,即只要类Finalizer被使用,即会触发相应的调用.这里使用的线程组是系统线程组,优先级也还算高,被配置为后台线程.

在使用jstack打印线程时,出现的如图下所示的线程,即是由这里来启动的.如下图所示

 

总结

整个Finalizer即是通过finalReference,由JVM和相应的java类相互配合来协同工作.并不是全部由jvm实现,因此可以认为其也并不是太底层的东西,而是为了实现相应的语义.一切都是正常的java来完成,由jvm配合.了解到整个过程,也是对java本身的运行机制有所了解.

相关文章

  • Spring @Bean注解深入分析源码执行过程

    Spring @Bean注解深入分析源码执行过程

    随着SpringBoot的流行,我们现在更多采用基于注解式的配置从而替换掉了基于XML的配置,所以本篇文章我们主要探讨基于注解的@Bean以及和其他注解的使用
    2023-01-01
  • JavaWeb实现学生管理系统的超详细过程

    JavaWeb实现学生管理系统的超详细过程

    学生信息管理系统是针对学校人事处的大量业务处理工作而开发的管理软件,主要用于学校学生信息管理,下面这篇文章主要给大家介绍了关于JavaWeb实现学生管理系统的超详细过程,需要的朋友可以参考下
    2023-05-05
  • 一个例子带你看懂Java中synchronized关键字到底怎么用

    一个例子带你看懂Java中synchronized关键字到底怎么用

    synchronized是Java里的一个关键字,起到的一个效果是"监视器锁",它的功能就是保证操作的原子性,同时禁止指令重排序和保证内存的可见性,下面这篇文章主要给大家介绍了关于如何通过一个例子带你看懂Java中synchronized关键字到底怎么用的相关资料,需要的朋友可以参考下
    2022-10-10
  • java中Map如何根据key的大小进行排序详解

    java中Map如何根据key的大小进行排序详解

    这篇文章主要给大家介绍了关于java中Map如何根据key的大小进行排序的相关资料,有时候我们业务上需要对map里面的值按照key的大小来进行排序的时候我们就可以利用如下方法来进行排序了,需要的朋友可以参考下
    2023-09-09
  • JAVA jvm系列--java内存区域

    JAVA jvm系列--java内存区域

    下面小编就为大家带来一篇基于jvm java内存区域的介绍。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-09-09
  • 解析Flink内核原理与实现核心抽象

    解析Flink内核原理与实现核心抽象

    Flink API提供了开发的接口,此外,为了实现业务逻辑,还必须为开发者提供自定义业务逻辑的能力,下面为大家解析Flink内核原理与实现核心抽象
    2021-08-08
  • springboot整合minio实现文件上传与下载且支持链接永久访问

    springboot整合minio实现文件上传与下载且支持链接永久访问

    本文主要介绍了springboot整合minio实现文件上传与下载且支持链接永久访问,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • mybatis单笔批量保存实体数据的方法

    mybatis单笔批量保存实体数据的方法

    这篇文章主要介绍了mybatis单笔批量保存实体数据的相关知识,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2018-01-01
  • Java SpringBoot核心源码详解

    Java SpringBoot核心源码详解

    这篇文章主要为大家介绍了Java SpringBoot核心源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • Java图片处理开源框架Thumbnailator

    Java图片处理开源框架Thumbnailator

    这篇文章主要为大家详细介绍了Java图片处理开源框架Thumbnailator的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05

最新评论