3. 常用排查工具介绍:Android Profiler、LeakCanary、MAT、JProfiler

在 ViewModel 持有 Activity 的场景中,仅靠代码审查很难发现潜伏的泄漏。本章引入四款工业级排查工具,并按照“现象识别 → 数据采集 → 原因定位 → 修复动作 → 复测 → 预防”的排查型课程骨架,说明每款工具在哪个阶段发挥核心作用,以及如何组合使用。

工具选择铁律:
LeakCanary 用于现象识别与复测;Android Profiler 负责数据采集与活证据获取;MAT 承担原因定位;JProfiler 在预防阶段做趋势与性能基线分析。四者按顺序递进,不可跳步。

3.1 现象识别阶段:LeakCanary 与 Android Profiler

工具 作用阶段 典型输出 判断规则
LeakCanary 现象识别 → 复测 堆转储通知 + “泄漏痕迹” 若通知出现 “com.example.MainActivity” 且引用链指向 ViewModel,则确认为 ViewModel 泄漏。
Android Profiler (Memory) 数据采集 → 复测 实时内存曲线 + HPROF 转储 Memory 曲线在 Activity.onDestroy() 后未明显下降,GC 后仍高于基线,视为可疑泄漏。
易错点:LeakCanary 默认只在 Debug 构建生效。release 包不会触发通知,不能代替性能监控。线上泄漏仍需依靠 Android Profiler 或自定义 HPROF 采集。

3.2 数据采集阶段:Android Profiler 获取 HPROF

当 LeakCanary 或用户反馈出现 “OutOfMemoryError” 时,立即使用 Android Profiler 捕获堆转储文件(HPROF)。操作步骤:

  1. 通过 USB 连接设备,选择目标进程。
  2. 点击 Dump Java heap,等待约 10–30 秒。
  3. 导出为 .hprof 文件(Profiler 自动转换或手动使用 hprof-conv)。
# 若导出为标准 .hprof,需先转换为 MAT 可识别格式
hprof-conv -z dump.hprof dump_standard.hprof
采集时机:必须正好在 Activity 进入后台并 GC 后执行 dump。最佳实践是先在 Profiler 中手动触发一次 GC(“Force garbage collection”),再立即 dump。只保留一次 dump 不能排除瞬时对象干扰,应连续采集两次 dump 对比。

3.3 原因定位阶段:MAT (Memory Analyzer Tool)

MAT 是定位泄漏根因的“解剖刀”。导入标准 HPROF 后,按故障树执行判断:

  • 第一步:检查 com.example.MainActivity 对象的 GC Root 路径(Path to GC Roots → exclude weak references)。
  • 第二步:若发现 ViewModel 实例通过 mActivityRef 强引用指向 Activity,则定位为 ViewModel 间接持有 Activity 引用。
  • 第三步:使用 Dominator Tree 查看 ViewModel 实例持有的重对象,检查是否包含 WebView、Fragment、Drawable 等大对象。
MAT 视图 排查表 判断规则
Histogram 按类名过滤泄漏 Activity 实例数 若 MainActivity 实例数 > 1,且每个实例都有不同 GC Root 路径,则为重复泄漏。
Path to GC Roots 逐层展开引用链 若路径中出现 ViewModel.store.mActivityRef → Activity,则判定为 ViewModel 泄漏。
Dominator Tree 查看 retained heap 最大的对象 若 ViewModel 实例 retained heap > 10MB,且子节点包含 Activity 自身,则必须修复。

3.4 修复动作验证:LeakCanary 复测

修复代码(如移除 ViewModel 内的 Activity 字段或改用 Application context)后,必须运行 LeakCanary 确认泄漏不再出现。

// 修复前:ViewModel 内部持有 Activity 引用
public class MyViewModel extends ViewModel {
    private Activity mActivity;  // 泄漏源
    public MyViewModel(Activity activity) {
        this.mActivity = activity;
    }
}

// 修复后:移除 Activity 引用,改为 Application context
public class MyViewModel extends ViewModel {
    private Application mApp;
    public MyViewModel(Application app) {
        this.mApp = app;
    }
}
  1. 重新编译并安装 Debug 包。
  2. 重复泄漏场景:打开→关闭 Activity 三次。
  3. 等待 LeakCanary 通知。若 10 秒内无通知,且 LeakCanary.INSTANCE.isLeakDisplayed() 返回 false,则修复成功。

3.5 预防阶段:JProfiler 与基线监控

JProfiler 用于 预防 阶段,在 CI 或压测环境中持续监控 ViewModel 的内存占用趋势。对比每次构建的堆快照,查看 ViewModel 实例数是否随测试次数线性增长。

指标 判断规则 触发动作
ViewModel 实例数 超过基线值的 20% 或持续 5 次构建递增 自动触发 JProfiler 的 HPROF dump,归档至 CI 制品。
Retained Heap 单次增长超过 5MB 发送告警给开发团队,要求排查新代码是否引入 Activity 引用。
预防最佳实践:在 JProfiler 中设置“ViewModel 最大实例数”告警规则,配合 GitLab CI / GitHub Actions 流水线,每次合并前自动执行一次内存快照比对。只有通过基线校验的 MR 才允许合并。
排查阶段与工具衔接图 现象识别 LeakCanary + Profiler 数据采集 Android Profiler → HPROF 原因定位 MAT → GC Root 路径 修复动作 移除 Activity 引用 复测 LeakCanary 验证 预防 JProfiler 基线监控 📘 工具组合排查策略 1. 现象识别阶段:LeakCanary 自动通知 → Android Profiler 实时确认 2. 数据采集阶段:Android Profiler 连续两次 dump(GC 前后) 3. 原因定位阶段:MAT 打开 HPROF → 过滤 Activity 实例 → Path to GC Roots 4. 修复动作阶段:根据 MAT 定位结果修改代码(移除 Activity 引用) 5. 复测阶段:LeakCanary 确认无新泄漏 6. 预防阶段:JProfiler 设置 ViewModel 实例数基线告警 判断规则 若 LeakCanary 通知指向 ViewModel + MAT 路径显示 Activity 弱引用 → 必为 ViewModel 泄漏。

以上四款工具的组合使用,覆盖从“泄漏发生”到“预防复发”的完整闭环。下节将基于 MAT 输出,讲解如何将 GC Root 链映射为代码中的具体引用关系。