1. 现象识别:缓存引用泄漏的典型信号
缓存引用导致的 Activity 泄漏不会立即引发崩溃,但会在用户操作和系统回收过程中留下清晰的可观测痕迹。你需要在三个维度上捕获异常信号,才能确认泄漏确实存在。
1.1 三大典型泄漏现象
| 现象 | 直观表现 | 触发时机 |
|---|---|---|
| Activity 无法回收 | 用户退出 Activity 后,内存中该 Activity 实例依然存活,重复进入退出导致实例数量不断累积 | 频繁页面跳转(如首页→详情→首页反复切换) |
| OOM 触发次数上升 | 低端设备在 3~5 个页面切换后直接闪退,adb logcat 出现 OutOfMemoryError | 连续打开新 Activity 或低内存设备上切换页面 |
GC 日志大量 ReferencePendingList | 每次 GC 的 MarkSweep 阶段出现大量待处理弱引用,清除后并无实际回收目标 | 手动触发 GC(如通过 System.gc() 或内存分析工具) |
⚠︎ 误判陷阱: 仅观察到 OOM 并不一定指向缓存引用泄漏。请先排除
Bitmap 过大、ViewPager 预加载等常规内存占用。只有当 GC 日志中出现持续增长的 ReferencePendingList 时,才能将怀疑转向缓存持有。1.2 GC 日志中的关键证据
在 Android Studio 的 Logcat 中过滤 GC_HEAP 或 MarkSweep,你会看到类似以下的输出:
D/dalvikvm: GC_CONCURRENT freed 258K, 4% free 6957K/7296K, paused 2ms+2ms, total 18ms
D/dalvikvm: GC_FOR_ALLOC freed 312K, 5% free 6892K/7296K, paused 15ms, total 16ms
D/dalvikvm: WAIT_FOR_CONCURRENT_GC blocked 3ms
D/dalvikvm: GAUSS_REF_PENDING 12 -> 34 // 注意这里
D/dalvikvm: GC_EXPLICIT freed 120K, 7% free 6810K/7296K, paused 2ms+2ms, total 19ms
其中 GAUSS_REF_PENDING(Android 8.0 后为 ReferencePendingList)的数值如果频繁出现且呈上升趋势,就说明有大量 WeakReference 或 SoftReference 所指向的对象虽然已被外部释放,但引用队列中的条目未能及时清除——典型缓存引用泄漏特征。
1.3 现象阶段:泄漏如何一步步呈现
下面这幅图概括了缓存引用泄漏从产生到 OOM 的完整现象链路:
现场排查建议: 当你在 Logcat 观察到
ReferencePendingList 数值连续增长时,立即执行一次 adb shell dumpsys meminfo <package>,对比退出 Activity 前后该进程的 Views 和 Activities 数量。如果 Activities 计数只增不减,即可确认泄漏存在。
1.4 本节小结
- 缓存引用泄漏的三大表现:Activity 无法回收、OOM 频率上升、GC 日志中
ReferencePendingList异常增长。 - 单一现象不足以定罪,必须同时满足至少两项,且排除其他常见内存膨胀原因。
- 下一章将进入数据采集阶段,使用 Android Profiler 和 MAT 堆转储文件精确获取泄漏对象详情。
✔︎ 现象识别完成标志: 已确认至少两个典型现象(例如 Logcat 中
ReferencePendingList 上升 + 退出后 Activity 数不变),并且记录了最初的观察时间点和设备型号。