Android踩坑经验-Finalize TimeoutException原因及解决方案

本文介绍了FinalizerDaemon和FinalizerWatchdogDaemon的工作原理及其可能导致的问题。FinalizerDaemon用于执行对象的finalize方法,而FinalizerWatchdogDaemon监视finalize方法执行时间,若超过限制将导致应用崩溃。文章还提供了解决方案及预防措施。

调用栈信息:
在这里插入图片描述
背景知识: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压力

### 回答问题: `Makefile:451: recipe for target 'target-finalize' failed` `make: [target-finalize] Error 1 (ignored)` --- ## ✅ 解释与分析 你看到的输出: ```text Makefile:451: recipe for target 'target-finalize' failed make: [target-finalize] Error 1 (ignored) ``` 表示: - 在执行 Makefile 第 451 行定义的目标 `target-finalize` 时,某个命令返回了非零退出码(即失败) - 但这个错误被 **make 忽略了**(注意最后是 `(ignored)`) 这意味着:**构建过程继续进行,并未终止。** --- ## 🔍 原因详解 ### 1. `recipe for target ... failed` - “recipe” 指的是 Makefile 中用于生成目标的一组命令。 - 当该目标下的某条 shell 命令返回非 0 状态码时,make 就会报告此错误。 例如: ```makefile target-finalize: @echo "Finalizing build..." @some_command_that_fails ``` 如果 `some_command_that_fails` 执行失败(返回非 0),就会触发这个错误。 --- ### 2. `(ignored)` 的含义 说明在 Makefile 中使用了 `-` 前缀或 `.IGNORE` 指令,或者上层 Make 调用中设置了忽略策略。 #### 示例:带 `-` 的命令(表示忽略错误) ```makefile target-finalize: - some_command_that_might_fail ``` 前面加 `-` 表示:“即使这条命令失败也不要停止”。 > 输出就是:`Error 1 (ignored)` --- ### 3. 常见出现在 `target-finalize` 的场景 `target-finalize` 通常是自定义目标,用于: - 清理临时文件 - 记录构建日志 - 发送通知 - 运行检查脚本 - 生成版本信息 这些操作往往不是关键路径,所以开发者会设计为“出错可忽略”。 --- ## ✅ 是否需要修复? | 情况 | 是否需要处理 | |------|---------------| | ✅ 构建产物正常生成、功能正常 | ❌ 不需要 —— 可安全忽略 | | ⚠️ 构建中途中断或缺少文件 | ✅ 需要排查 —— 错误可能隐藏问题 | | 🛑 日志显示关键步骤失败 | ✅ 必须修复 | 👉 所以你要判断: > 这个 `target-finalize` 做了什么?它失败会影响最终镜像或程序吗? --- ## ✅ 如何排查? ### 步骤 1:查看 Makefile 第 451 行附近内容 打开 Makefile,跳转到第 451 行: ```makefile # 示例(可能类似这样) target-finalize: $(Q) ./scripts/finalize-build.sh $(TARGET) $(Q) echo "Build completed at $$(date)" >> build.log ``` 假设 `finalize-build.sh` 不存在或权限不足,则执行失败 → 报错。 --- ### 步骤 2:手动运行对应命令 尝试在终端运行那个失败的命令(复制出来): ```bash ./scripts/finalize-build.sh your_target_name ``` 观察是否报错: - 权限问题?→ `chmod +x finalize-build.sh` - 路径问题?→ 改为绝对路径或修正相对路径 - 工具缺失?→ 安装依赖 --- ### 步骤 3:决定是否保留“忽略” #### 如果是非关键任务(如日志记录),可以保持忽略: ```makefile target-finalize: - ./scripts/optional-notification.sh ``` #### 如果是关键步骤,请移除 `-` 并修复错误: ```makefile target-finalize: ./scripts/mandatory-final-step.sh # 不允许失败 ``` --- ## ✅ 总结 - `Error 1 (ignored)` 表示命令失败但被忽略 - 多数情况下不影响主流程 - 你需要确认:**这个目标是否重要?** - 若不重要,可忽略;若重要,需修复脚本或权限问题 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值