第1章 泄漏现象识别

LiveData Observer 未移除是 Android 开发中最容易忽视的内存泄漏场景之一。Observer 持有 Activity 或 Fragment 的匿名内部类引用,若在页面销毁后未主动移除,将导致页面实例无法被 GC 回收,进而引发一连串可观测的负面现象。

本章聚焦于 现象识别 阶段,帮助开发者快速通过运行时指标和工具判断是否发生了 Observer 泄漏。

1.1 泄漏现象一览

可观测现象 典型表现 判断依据
内存持续增长 反复进入/退出页面后,总内存(Native+Java)曲线稳步上升,从不回落至基线 使用 Memory Profiler 观察多次操作后的堆内存折线图
GC 频繁 Logcat 中出现连续 ConcurrentGC 或 AllocGC,间隔小于 1 秒 日志搜索 GC freed,统计每分钟 GC 次数
OOM 风险 高版本 Android 抛出 OutOfMemoryError;低版本出现 ExceptionInInitializerError Crash 报告或 Logcat 中 OutOfMemoryError 堆栈
Activity 实例未释放 退出页面后,Activity#onDestroy 已调用,但 Activity 对象仍存在于堆中 Dump HPROF 后搜索 Activity 全类名,检查实例数
经验规则:若退出页面后托管了 LiveData 的 Activity 实例数 > 1,且 Observer 被生命周期为 “ALWAYS” 的 LifecycleOwner 注册,即可高度怀疑 Observer 未移除泄漏。

1.2 故障树:从现象到可疑原因

以下故障树帮助你在定位时快速缩小范围:

现象:内存曲线持续上升且不下降
├─ 是 → GC 日志是否频繁(> 10次/分钟)?
│   ├─ 是 → 堆转储中 Activity 实例数 > 1?
│   │   ├─ 是 → Observer 未移除(本章重点)
│   │   └─ 否 → 检查其他静态引用泄漏(全局单例、列表等)
│   └─ 否 → 检查图片/视频资源泄漏或 Native 内存泄漏
└─ 否 → 观察是否有 OOM 崩溃?
    ├─ 是 → 确认崩溃堆栈是否包含 LiveData 相关类
    └─ 否 → 正常应用内存波动

1.3 排查表:三步快速判定

  1. 第一步:打开 Memory Profiler,多次进入/退出目标页面,观察 “Java Heap” 折线 —— 若每次退出后堆内存没有回到操作前的基准值,说明有对象驻留。
  2. 第二步:抓取 HPROF dump,搜索 Activity 类名,检查实例数量。正常应为 0(已销毁);若存在 1 个或更多,则 Activity 实例被意外持有。
  3. 第三步:查看保留引用链,在 HPROF 中展开 GC Root 路径,若看到 LiveData.mObservers 或 ObserverWrapper,即可确认 Observer 未移除。

1.4 现象识别流程图

LiveData Observer 泄漏现象识别流程 应用卡顿 / 界面响应慢 内存持续增长 + GC 频繁(Logcat 可见) OOM 风险 / 崩溃 Activity 实例未释放(HPROF 确认) 根源:LiveData Observer 未移除 排查要点 1. 使用 Memory Profiler 2. 观察 GC 日志 3. 分析 HPROF 引用链 注意 退出页面后 Activity 实例数 > 0 即为异常

1.5 典型日志示例

使用 adb logcat -s GC 可实时查看 GC 行为。以下是一条频繁 GC 的日志示例:

I/art: Background GC all threads suspended took 13.294ms
I/art: Background GC all threads suspended took 11.122ms
I/art: Background GC all threads suspended took 14.005ms
I/art: Alloc concurrent mark sweep GC freed 2(128KB) AllocSpace objects, 0(0B) LOS objects, 12% free, 14MB/16MB, paused 1.805ms total 17.094ms

若在退出页面后,该日志持续输出且 14MB/16MB 比例的 分配后堆大小 不回落,说明存在长期存活的对象。

⚠ 常见误区:只靠 “内存增长” 判断泄漏可能误报。必须结合 Activity 实例数 和 引用链 双重确认。LiveData Observer 泄漏的特殊性在于:Observer 持有 LifecycleOwner 的强引用,而 LiveData 通过 mObservers 持有 Observer,形成 LiveData → Observer → Activity 的泄漏链。

1.6 小结

本章明确了 Observer 未移除泄漏的四种核心现象:内存持续增长、GC 频繁、OOM 风险以及 Activity 实例未释放。通过故障树、排查表和流程图,你可以快速从应用卡顿逐步追溯到 LiveData 的 Observer 残留。下一阶段(第2章起)将深入数据采集和引用链分析,但前提是你能准确识别出上述现象。