java gc与finalize_finalize 与 Java GC

闲逛ITEye时看到了译帝的一篇翻译博客,其中提到了关于Java类重写finalize方法后带来的诡异的GC overhead limit问题。博客的结尾非常详细的说明了这个问题产生的原理,但是始终有一个地方没有得到清晰的答案:由于finalize方法是Object类的protected方法,即无论重写与否,所有的Java类都会带有finalize方法,但为什么只有重写之后才会出现GC问题,不重写与重写的真实差别到底在哪儿?

通过思考始终得不到答案,索性打开Eclipse直接调试代码:

首先验证了博客中的现象的确可以重现。

中间一度怀疑问题的原因可能是finalize方法中引用了类的静态变量AtomicInteger会引起GC的问题(实际上这个方向是错误的,GC是根据对象是否存在被引用关系来判断对象是否回收,惭愧),删掉原来的方法体然后随意在finalize方法中声明了一个变量,结果运行时问题仍然存在。

到了此时,开始怀疑是不是和finalize方法体有关系了,直接删除掉finalize方法体中的所有内容,运行后发现问题真的不存在了!

由于出现GC问题时内存堆中存在大量Finalizer对象,而Finalizer对象只能通过Finalizer类的静态register方法创建,因此尝试在register方法中加上断点对两种情况分别运行,发现方法体为空时,register方法不会被调用,这样自定义对象便没有被任何对象引用,可以轻松的被GC回收掉。

由于register方法是被VM所调用,只能求助于莫枢大神,而莫枢也确认了只要Java类以及它所有的祖先类中不含有finalize方法或者finalize方法体为空时,VM便不会将该类的对象实例注册为finalizable对象(地址)。

至此,算是把整个问题的来龙去脉给整理的差不多了:

JVM创建自定义对象。

JVM检测对象类以及祖先类是否含有非空的finalize方法定义,如果均没有,则不进行后续的Finalizer相关处理。

JVM调用Finalizer类的静态register方法,创建包含该对象引用的Finalizer对象,同时Finalizer静态类将该Finalizer对象加入到一个单向链表中。

运行过程中,JVM会将这些Finalizer对象的状态更新为pending(这部分可以参考java.lang.ref.Reference类中的说明)。

Reference类中的ReferenceHandler线程会扫描到这些状态为pending的Finalizer对象,将这些对象enqueue到Finalizer静态类引用的ReferenceQueue(非Finalizer中的单向链表)当中。

Finalizer线程从ReferenceQueue中逐一弹出Finalizer对象,首先将Finalizer对象从Finalizer的单向链表中删除,解除了Finalizer静态类对Finalizer对象的引用关系,之后调用Finalizer对象引用的自定义对象的finalize方法,Finalizer对象以及自定义对象此时均可被GC回收。

由于Finalizer线程的低优先级,可能引起旧对象的释放速度无法跟上新对象的创建速度,引起OutOfMemory问题(例子中的GC overhead limit原因是由于新对象创建的代价太低而旧对象回收的代价较高导致CPU用于GC回收的时间比例超过98%)

最后,网易研究院的马进在他的博客中非常详细的阐述了finalize的原理以及因此引发的案例,也非常值得一读。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值