调用栈信息:
背景知识:FinalizerDamemon和FinalizerWatchdogDaemon
FinalizerDamemon
析构守护线程,重写了finalize的对象,在创建时会新建一个FinalizerReference,这个对象是强引用类型,封装了override finalize()的对象,下面直接叫原对象。原对象没有被其他对象引用时(FinalizeReference除外),执行GC不会马上被清除掉,而是放入一个静态链表中(ReferenceQueue),析构守护线程获得时间片后,弹出链表对象,并执行原对象finalize()方法,对应的FinalizerReference对象在下次执行GC时就会被清理掉。
FinalizerWatchdogDaemon
当一个实例的finalize函数,花费超过MAX_FINALIZATION_MILLIS(默认10s,很多第三方厂商改为60s),则FinalizerWatchdogDaemon线程会让VM退出,应用程序程序crash
原因:
1:Finalize对象累积太多,导致FinalizerDaemon线程来不及处理
Q:项目中没有类重写finalize方法,什么对象累积太多?
可能项目中没有类重写finalize方法,但Android系统中有非常多的类实现了finalize方法,如:Binder,View,Matrix,Bitmap,AssetManager等
2:运行过程中息屏,或者某些省电模式下,系统CPU降频,FinalizerDaemon线程获得时间片短且执行速度慢导致超时
3:某个析构函数调用阻塞
解决方法:
这个问题,并不像NPE那样,可以快速定位解决,甚至来说,这个问题几乎无解。但可以通过反射调用让FinalizerWatchdogDaemon停止工作,从而让这种crash消除掉。
(现在Android版本不断升级,P以上对反射使用有限制,因此无法通过反射停止FinalizerWatchdogDaemon线程,但Android P的机器目前都是高端机,性能较好的机器出现此问题的概率本身就比较小,因此基本能解决finalize timeout问题)
参考代码:
try {
final Class clazz = Class.forName("java.lang.Daemon$FinalizerWatchdogDaemon");
final Field field = clazz.getDeclaredField("INSTANCE");
field.setAccessible(true);
final Object watch = field.get(null);
try {
final Field thread = clazz.getSuperclass().getDeclaredField("Thread");
thread.setAccessible(true);
thread.set(watch,null);
} catch (final Throwable t) {
Log.e(TAG, "stopWatchDog: set null error" + t);
t.printStackTrace();
try {
final Method method = clazz.getSuperclass().getDeclaredMethod("stop");
method.setAccessible(true);
method.invoke(watch);
} catch (final Throwable e) {
Log.e(TAG, "stopWatchDog: stop error" + t);
t.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
还有一些理论上可能有帮助的措施:
1:减少内存占用,避免不必要的对象创建
2:消除内存泄露问题,缓解GC压力