1. 泄漏现象识别
Retrofit 回调泄漏并不会像崩溃那样立即暴露,而是通过一系列渐进式性能症状逐渐显现。本节建立一套可观测的“症状‑故障”对应表,帮助你在第一现场锁定可疑的内存问题。
1.1 四种典型现象的现场特征
| 现象 | 用户可感知表现 | 工具证据 | 优先排查方向 |
|---|---|---|---|
| 内存抖动 | 列表滑动卡顿、动画掉帧 | Profiler 内存图锯齿状;Allocation Tracking 显示大量临时对象 | 检查回调内部是否频繁创建匿名内部类或短生命周期对象 |
| GC 频繁 | 操作响应延迟、应用间歇性无响应 | Logcat 中出现 GC_CONCURRENT / GC_FOR_ALLOC 间隔 < 1s |
查看 GC 根引用,确认是否有大量 Activity/Fragment 被回调隐性持有 |
| OOM | 应用突然退出,或抛出 OutOfMemoryError |
堆转储分析显示 Retrofit / OkHttp 相关对象占大量连续空间 |
抓取 hprof,过滤 Retrofit 回调 + 外层容器实例数 |
| LeakCanary 报告 | 通知栏提示“泄漏已检测” | LeakCanary 自动生成 LeakTrace,指向 Callback → Lifecycle 闭环 |
直接查看泄漏链中是否出现 Retrofit.Callback 与 Activity / Fragment 的强引用路径 |
1.2 判断规则:快速定位泄漏概率
使用以下条件式决策表,当多个症状同时出现时,泄漏概率显著升高:
- 规则 1: 仅内存抖动,无 GC 频繁 → 低概率(临时对象过多,但未形成泄漏)
- 规则 2: 内存抖动 + GC 频繁 → 中概率(对象未释放,但可能被弱引用 )
- 规则 3: 内存抖动 + GC 频繁 + 某 Activity 退出后仍有该 Activity 实例 → 高概率(Retrofit 回调持有 Activity 引用)
- 规则 4: LeakCanary 报告直接确认为泄漏 → 必修复优先级
现场数据采集清单(启动阶段)
一旦发现上述任一现象,立即执行:
- 打开 Android Profiler 录制内存 / CPU 片段(持续 30 s 以上)
- adb 抓取 logcat 并过滤
GC_* | "LeakCanary" - 通过
adb shell dumpsys meminfo获取堆摘要 - 如已集成 LeakCanary,查看
LeakTrace中的引用链
1.3 快速验证日志示例
以下 logcat 输出典型地揭示了泄漏早期的 GC 风暴:
2025-03-21 14:23:45.123 I/art : GC_CONCURRENT freed 4MB, 68% free (8MB/12MB), paused 1.2ms total 15ms
2025-03-21 14:23:45.456 I/art : GC_FOR_ALLOC freed 1MB, 78% free (7MB/9MB), paused 1ms total 11ms
2025-03-21 14:23:45.789 I/art : GC_CONCURRENT freed 3MB, 62% free (6MB/10MB), paused 0.9ms total 13ms
如果此类日志在用户操作间隙(如页面切换后 5 秒内)持续出现,且对应 Activity 已 onDestroy 但 Profiler 中仍存在该 Activity 实例,则基本确认 Retrofit 回调泄漏。
✨ 技巧: 使用
adb shell dumpsys meminfo 查看 ViewRootImpl 和 Activity 的计数。正常退出单页面后,相关 Activity 计数应归零,否则就是泄漏的直接证据。