第4章:观察内存趋势图——PSS、Java Heap、Native Heap、堆增长模式

现象识别:内存趋势图中的危险信号

在 Android Profiler 的 Memory 面板中,正常的应用内存会呈现“锯齿形波动”——分配内存后回收,峰值稳定。当出现以下三种模式时,高度疑似内存泄漏:

  • 持续上升型:PSS 或堆大小随操作次数线性递增,从不回落。
  • 阶梯型:每次操作后内存上升一级,GC 后不完全下降,形成台阶。
  • 锯齿不回缩型:频繁 GC 导致频繁锯齿,但锯齿底部逐渐抬高。
❗ 易混淆场景:单次大对象分配后触发 GC,内存陡升陡降属正常。危险信号是“底部不回缩”或“斜率持续为正”。

数据采集:在 Profiler 中锁定三个关键指标

点击 Memory Profiler 中的录制按钮,选择 “Record allocation” 以同时捕获堆栈。重点关注以下三个指标:

指标含义采集技巧
PSS (Proportional Set Size)应用实际占用的物理内存(按比例分配共享库)观察整体趋势,若持续增长说明存在泄漏
Java HeapDalvik/ART 堆中的 Java 对象占用量逐次触发操作后 dump,对比 Java Heap 变化
Native Heap由 C/C++ 代码通过 malloc/new 分配的内存若 Java Heap 稳定但 Native Heap 上涨,排查 JNI 泄漏
# 命令行辅助采集(可选)
adb shell dumpsys meminfo <包名> | grep -E "PSS|Java Heap|Native Heap"
采集节奏:重复执行 5~10 次关键操作(如页面切换、图片加载),每次操作后停顿 2 秒让 GC 完成,观察趋势是否回落。

原因定位:基于趋势图的故障树与判断规则

使用以下故障树快速收缩泄漏范围:

  • PSS 持续上升 + Java Heap 同步上升 → Java 堆内存在泄漏(Activity/Fragment 未释放、静态集合类持有 Context)。
  • PSS 持续上升 + Java Heap 稳定 → 排查 Native Heap 或匿名共享内存(Bitmap 未 recycle、WebView 泄漏、JNI 全局引用未删除)。
  • PSS 或堆在 GC 后底部抬高 → 泄漏对象被 GC Root 持有,每次操作产生新对象无法回收。
# 判断规则口诀
Java堆涨 → 查引用
Native堆涨 → 查 JNI / Bitmap / WebView
PSS涨但堆图稳 → 查 mmap / 线程 / 匿名页
内存趋势图排查故障树 趋势图异常 PSS 持续上升 Java Heap 上升 Native Heap 上升 检查 Java Heap & Native Heap 两者皆升 → Java泄漏 Java Heap 稳定? 是 → 排查 Native / Bitmap / WebView Native Heap 专属排查 检查 JNI / 共享内存 Java 泄漏类型 Activity泄漏 / 静态集合 / 内部类 Native 泄漏类型 未释放的 malloc / JNI 全局引用 修复:释放引用 / 回收资源 复测:趋势图回归正常

修复动作:针对趋势图特征的精准修复

  • Java Heap 阶梯上升 → 使用 LeakCanary 或 MAT 分析 hprof,定位被 GC Root 引用的泄漏对象(常见:Runnable、View.OnClickListener 匿名内部类持有 Activity)。
  • Native Heap 持续增长 → 检查 BitmapFactory 是否调用 recycle()(Android 8.0+ 需手动释放 native 内存);检查 JNI 全局引用是否及时 DeleteGlobalRef。
  • PSS 异常但堆图正常 → 排查线程创建数量、内存映射文件(mmap)、WebView 渲染进程泄漏(单独进程需主动销毁)。
案例:某电商 App 在商品详情页反复切换后,PSS 从 80MB 升至 280MB。趋势图显示 Java Heap 稳定在 40MB,Native Heap 从 20MB 升至 120MB。定位为 WebView 每次创建新 WebViewClient 未及时 destroy(),修复后趋势图稳定在 100MB。

复测:验证修复效果

  1. 重新录制内存趋势,重复 10 次操作。
  2. 确认 PSS 在 GC 后能回落至基线水平(浮动不超过 5%)。
  3. 检查 Java Heap 和 Native Heap 不再出现“底部抬高”或“线性上升”。
  4. 使用 adb shell dumpsys meminfo 对比修复前后峰值。

预防:建立内存趋势基线

  • 在 CI 中集成 Memory Profiler 自动录制,每次 UI 测试后导出 trend 报告。
  • 设定 PSS 和 Heap 阈值警报,例如 Java Heap 峰谷差超过 20% 即标记。
  • 代码审查时要求对 Bitmap、Handler、WebView 等资源使用时添加显式释放注释。
工具建议:使用 simpleperf record + meminfo 脚本实现趋势图自动化分析;配合 LeakCanary 2.x 自动捕获泄漏堆栈。