问题起因
在解决应用内存占用久高不下的问题时,常常会发现排名第一的是一个叫做FinalizerReference的东西
下图是项目中内存快照在AndroidStudio2.4中打开看到的样子
可以看到,占用排名第一的类FinalizerReference占用了59M之巨(内存快照前有使用AS强制GC多次)
这就很让人好奇了
FinalizerReference是什么?
我们来看源码:
在讲Java 虚拟机 JVM垃圾回收机制时会涉及到一个概念,即finalize方法,这是Sun公司在Java设计之初,为照顾c++程序员有写析构函数的习惯而做的一种妥协
在《深入理解Java虚拟机》一书中,讲到GC在最终回收对象之前,会去检查对象是否override了finalize方法,如果有覆盖此方法,对象就会被添加到finalize执行队列中,该书中没有指明是什么队列,实际上finalize执行队列就是我们这里讨论的FinalizerReference(书中还有提到finalize方法只会执行一次,并且有超时保护,因与本文主题无关,不做展开讨论)
也就是说,有重写Java根基类Object的finalize方法的所有对象,在被最终被GC回收之前,都会被添加到这个队列中,等待被执行
为什么不能被GC
这里要谈到JVM设计的一个基本标准
GC的finalize执行队列会在一个单独的守护线程中运行,这一线程的优先级极低;
一旦用户创建的对象速度过快,含finalize的对象速度快于finalize队列移除各元素的速度,FinalizerReference就会越来越大,而它获取CPU时间又少的可怜
测试中发现,1~2h过去后,FinalizerReference的大小并没有按照预计的回落到较低水准
这就迫使我们一定要解决这一问题,否则,随着应用开辟的内存越来越大,很容易引发喜闻乐见的OutOfMemoryError
如何解决
- 不要重写finalize()方法,实在要释放资源,请到其它destroy一类函数中处理
实际上Java库中不少方法都有重写finalize方法(如Input/outputStream、Canvas、Paint)
这就引出了第二条指导原则:
重复利用资源(避免反复创建)
杀器FinalizerHelper
- 此工具来源于公司同事的技术分享
- 指导思想:在场景退出的安全节点,利用反射强制调用FinalizerReference的remove方法,将对象从队列中移除掉
- 对具体实施细节的同学联系我