Android Memory Analysis with MAT进阶篇

438 阅读3分钟

一、MAT工具:内存分析的“瑞士军刀”

1. 安装与启动

  • 下载地址:

    Eclipse Memory Analyzer

    Eclipse Memory Analyzer

  • 核心功能模块:

    • Overview:内存快照概览(总大小、类数量等)
    • Histogram:对象直方图(按类统计实例数)
    • Dominator Tree:支配树(按内存占用排序)
    • OQL控制台:对象查询语言(精准定位问题对象)

2. 内存快照捕获技巧

  • 触发GC:在dump前手动触发GC(Debug.startAllocTracing()),减少噪声数据
  • 过滤系统库:在MAT中勾选Keep unreachable objects,避免系统库干扰分析
  • 多快照对比:连续操作后多次dump,通过对比定位内存增长点

二、内存分析“四步法”

Step 1:概览分析

  • 检查Total CountTotal Size是否异常(如超过200MB需警惕)
  • 关注Retained Heap占比高的类(尤其是自定义类)

Step 2:Dominator Tree定位

  • Retained Heap降序排列

  • 展开前十大对象,检查是否包含:

    • 未销毁的Activity/Fragment
    • 缓存未释放的Bitmap/大数组
    • 第三方SDK的意外残留

Step 3:路径追踪

  • 右键可疑对象 → Path To GC Roots → 排除Weak/Soft/Phantom引用

  • 典型泄漏路径示例:

    	MyActivity -> View -> Handler -> MessageQueue -> Looper -> Thread
    

Step 4:OQL精准打击

  • 常用查询语句:

    sql
    	-- 查找所有未关闭的Cursor
    
    	SELECT * FROM android.database.Cursor WHERE isClosed() = false
    
    	 
    
    	-- 统计Bitmap数量及大小
    
    	SELECT toString(s), sizeof(s) FROM INSTANCEOF android.graphics.Bitmap s
    
    	 
    
    	-- 查找持有Activity的匿名内部类
    
    	SELECT * FROM INSTANCEOF java.lang.ref.WeakReference WHERE toString().contains("Activity")
    

三、线程分析:揪出内存抖动的元凶

1. 线程视图入口

  • Thread Overview:展示所有线程及其状态

  • 重点关注:

    • Runnable线程数量(正常应<10)
    • 线程栈中的自定义类(如MyTask

2. 典型问题场景

  • 线程池泄漏

    	ThreadPoolExecutor -> Worker -> Runnable -> MyTask -> MyActivity
    

    修复方案:使用Executors.newSingleThreadExecutor()替代手动管理线程。

  • AsyncTask未取消

    	AsyncTask$1 -> MyActivity
    

    修复方案:在onDestroy中调用cancel(true)

四、对比分析:量化内存变化

1. 操作步骤

  • 生成操作前后的两个.hprof文件
  • 在MAT中打开Compare Basket视图
  • 添加两个快照到对比篮
  • 重点关注Diff列变化,定位内存增长点

2. 实战案例
某应用在连续切换页面后内存增长50MB,通过对比分析发现:

  • MyAdapter$ViewHolder实例数从10激增至500
  • 进一步追踪发现RecyclerView未正确复用ViewHolder
  • 修复方案:检查getItemViewTypeonBindViewHolder逻辑,确保复用机制生效

五、进阶技巧:内存问题的“根因分析”

1. 支配树优化

  • 右键点击类 → List Objects → with incoming references
  • 展示所有引用该对象的入口,快速定位泄漏源

2. 合并路径分析

  • 右键多个可疑对象 → Merge Shortest Paths to GC Roots
  • 找出共同引用链,定位公共泄漏点

3. 正则表达式过滤

  • OQL中使用正则表达式:

    sql
    	SELECT * FROM INSTANCEOF java.lang.String WHERE toString().matches(".*leak.*")
    

六、总结:MAT工具的“降维打击”

MAT工具就像内存分析的“显微镜”,通过:

  • Dominator Tree定位内存大户
  • OQL查询精准扫描问题对象
  • 线程分析揪出资源泄漏
  • 对比分析量化内存变化

掌握这些技巧后,即使是复杂的内存泄漏问题也能迎刃而解。记住:内存优化没有银弹,但MAT绝对是你最趁手的“内存猎刀”。日常开发中建议养成以下习惯:

  1. 关键操作后手动触发GC并dump内存
  2. 定期进行自动化内存测试
  3. 对第三方库进行内存安全审查

通过系统化的内存分析,可以让应用在流畅度与稳定性上实现质的飞跃。

参考文档

Android 内存优化之二 - MAT使用进阶 · Android Performance