目录
[显示]

1.摘要

前一阵排查一个跟java的finalizer有关的问题,发现网上虽然有很多关于finalizer的描述,但是大多都语焉不详,草草说了几句“带finalize的对象会进入finalizer队列”然后就没下文了,这让我研究了很久也没搞明白这个finalizer队列究竟是什么原理,也没明白为什么heap里面的Finalizer对象非常多但是用jmap -finalizerinfo的时候总是显示为0,最后只好看jdk源代码解释这个问题。代码基于openjdk6。

2.finalize()和Finalizer的创建

首先,如果某个类Override了finalze方法的话,parse这个class时会把_has_finalizer置为TRUE。

share/vm/classfile/classFileParser.cpp

在创建对象时,会在has_finalizer=true时调用register_finalizer。 share/vm/oops/instanceKlass.cpp

在register_finalizer里调用了finalizer_register_method,这个method指向Finalizer类的register方法:

Finalizer对象用next和prev指针维护了双向链表,unfinalilzed变量实际是链表的表尾,并声明了一个静态变量queue,可以看到在构造函数里调用了父类的构造函数和add()

FinalReference也调用了父类的构造函数

Reference类里有一个本地成员变量queue,调用构造函数时这个变量被赋值为Finalizer的静态queue。

到这里可以小结一下: 1. 在创建对象时,如果对象override了finalize()方法,jvm会同时创建一个Finalizer对象 2. 所有Finalizer对象组成了一个双向链表 3. 所有Finalizer对象都有一个名为queue的成员变量,指向的都是Finalizer类的静态Queue。

3.Finalizer的销毁和Finalizer Queue

那么再看一下这些变量和queue有什么用,由于线上是old区cms回收的问题,这里就以cms为例:

先从cms回收器的入口开始,默认的cms回收器是由一个后台的thread执行的,只挑重点。 share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp

这里调用到了CMSCollector的collect_in_background函数: share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp

在FinalMarking里执行了final_remark_op

share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp

真正的执行逻辑在do_CMS_operation:

调用了checkpointRootsFinal,之后一路徘徊到ReferenceProcessor的enqueue_discovered_references:

在referenceProcessor里又是一顿调用:

share/vm/memory/referenceProcessor.cpp

在enqueue_discovered_reflists里引用的_discovered_refs类似邻接表,数组中每个元素指向一个链表,链表中每个节点是一个需要被回收掉的对象。
最后会来到这个函数:

enqueue_discovered_reflist函数把所有节点的next指向自己,并把节点插入到pending_list_add的位置,这个pending_list_addr是jvm硬编码写死的,定义在:

share/vm/classfile/javaClasses.cpp

查看java.lang.ref.Reference类也找到了这个定义:

在Reference内部启动了一个线程,用来处理这个pending list:

并且这个线程的级别是最高的:

线程会把pending对象所指的reference移出链表,如果对象的queue不是空,则把对象放到queue中。对于finalizer对象来说,这个queue是之前提到的finalizer类的静态变量queue,在Finailzer类中也有一个对应的处理线程:

4.总结

最后总结一下finalizer的生存周期:

  1. 在创建对象时,如果对象override了finalize()方法,jvm会同时创建一个Finalizer对象
  2. 所有Finalizer对象组成了一个双向链表
  3. 所有Finalizer对象都有一个名为queue的成员变量,指向的都是Finalizer类的静态Queue。
  4. cms gc执行到mark阶段的最后时,会把需要gc的对象加入到Reference的pending list中。
  5. 有一个专门的高级别线程Reference Handler处理pending list,把pending list中的对象取出来,放到这个对象所指的Reference Queue中,对于Finalizer对象来说,这个queue指向Finalizer类的静态Queue。
  6. Finalizer类有一个专门的线程负责从queue中取对象,并且执行finalizer引用的对象的finalize函数。

jvm的代码还是非常复杂的,感觉这次看的还是太粗略,会有不少疏漏,过段时间得抽空完整的了解一下jvm源代码。