第4章 堆转储获取实战
在Kotlin内存泄漏排查中,堆转储文件(Heap Dump/Hprof)是还原泄露路径、定位泄漏引用的核心证据。本章聚焦四种实战获取手段:Android Profiler导出、adb命令抓取、OOM时自动dump以及转储文件压缩与传输。通过现象识别→数据采集→原因定位→修复动作→复测→预防的闭环,掌握不同场景下如何稳定、高效地获取堆转储。
前置条件:确保设备已开启开发者选项、USB调试;Android Profiler仅支持Android 5.0(API 21)及以上;自动dump需要应用代码支持或使用LeakCanary等工具。
4.1 现象识别:什么情况下需要抓取堆转储?
- OOM崩溃:日志中出现
OutOfMemoryError、java.lang.OutOfMemoryError: Failed to allocate等关键字。 - GC频繁导致卡顿:Systrace或Logcat显示GC次数>20次/分钟,且内存无法稳定回收。
- 内存曲线持续上升:Android Profiler或dumpsys meminfo显示进程PSS每5分钟增长超过50MB。
- LeakCanary检测到泄漏:LeakCanary通知已有对象泄漏,需获取原始hprof做深度分析。
注意:堆转储会暂停应用(约200ms~1s),不要在用户核心操作路径上频繁触发;生产环境建议配合条件判断(如仅当内存>80%阈值时触发)。
4.2 数据采集:四种获取方式详解
下面通过流程图总览四种方式的接入点与决策路径:
4.3 四种获取方式详解
4.3.1 Android Profiler导出
适用于开发阶段,已连接Android Studio,可交互查看对象树。
- 打开Android Studio > View > Tool Windows > Profiler。
- 选择目标设备和进程,点击Memory时间线。
- 点击Dump Java heap按钮(绿色垃圾桶图标旁)。
- 等待几秒,自动生成.hprof文件并显示在Profiler下方。
- 右键选择Export to file...保存。
注意:Profiler导出的.hprof是Android格式,不能直接被MAT识别。需先通过
hprof-conv 工具转换为标准Java堆转储:hprof-conv -z input.hprof output.hprof
4.3.2 adb命令抓取
适合无AS环境、CI自动化、批量抓取。无需root。
| 场景 | 命令 | 说明 |
|---|---|---|
| 抓取指定进程 | | 进程名必须完整;文件保存到设备。 |
| 拉取到本地 | | 注意权限:部分设备需先 adb shell chmod 777 /sdcard/dump.hprof |
| 支持重复抓取 | | /data/local/tmp 避免权限问题。 |
陷阱:
am dumpheap 只抓取Java堆,不包含Native堆;若怀疑Native内存泄漏需另用 am nativeheap 或Profiler的Native内存分析。
4.3.3 OOM自动dump
在生产环境捕捉崩溃前的堆状态。通过注册全局未捕获异常处理器实现。
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
if (throwable is OutOfMemoryError) {
val pid = Process.myPid()
val path = "${context.cacheDir}/oom_${pid}.hprof"
// 注意:OOM时可能无法分配Java内存,推荐在JNI层用C++去dump
// 简易方案:fork process后执行 am dumpheap
val cmd = "am dumpheap $pid $path"
Runtime.getRuntime().exec(cmd)
// 保存现场后可以立即崩溃或等待dump完成
}
// 调用原handler
originalHandler?.uncaughtException(thread, throwable)
}
更可靠的自动dump方案可使用LeakCanary:在应用初始化时配置,当检测到对象未被回收时自动抓取。
4.3.4 转储文件压缩与传输
原始.hprof文件可能超过200MB,传输前必须压缩。
| 方法 | 命令/工具 | 压缩率 |
|---|---|---|
| 设备端gzip | | 40%~60% |
| 本地压缩 | | 30%~50% |
| 分次拉取 | | 大文件可断点续传(配合adb shell split) |
| 网络直传sftp | | 需设备已连内网或使用代理 |
4.4 原因定位:选型判断规则
根据实际环境和目的,按以下故障树选择采集手段:
- 开发/调试 → 首选Android Profiler,交互直观,可实时查看对象。
- 回归测试/CI → 使用adb命令脚本化,批量抓取。
- 线上OOM问题 → 使用LeakCanary或自定义UncaughtExceptionHandler自动dump。
- 快速分析需求 → 直接adb dumpheap并拉取,无需安装AS。
- 文件过大无法传输 → 设备端先gzip压缩再adb pull。
判断规则速查表:
| 条件 | 推荐方法 | 关键约束 |
|---|---|---|
| 已连接AS | Android Profiler导出 | 需暂停应用 |
| 无GUI环境 | adb命令 | 需知道包名 |
| 生产崩溃场景 | OOM自动dump | 注意OOM时内存紧张,优先JNI层方案 |
| 泄漏检测工具 | LeakCanary自动获取 | 仅当配置LeakCanary后生效 |
4.5 修复动作:常见问题与调整
- hprof文件损坏:adb pull时文件被截断 → 重新抓取,或改用
adb pull --sync保证完整性。 - dropheap失败:进程无权限或已死亡 → 确认进程是否在运行,使用
adb shell ps | grep com.your.app检查。 - 自动dump重复触发:在UncaughtExceptionHandler中加标志位,避免递归。
- 压缩后体积仍大:改用
gzip --fast加快速度,或用xz获得更高压缩率(需设备支持)。
4.6 复测与验证
- 抓取成功后,确认文件大小非零,可通过
ls -lh检查。 - 将.hprof.gz解压后使用MAT打开,验证能显示对象树。
- 对比多次抓取的内存快照,确认内存曲线是否持续上升。
- 若使用自动dump,模拟OOM场景(如大量加载图片)验证触发。
4.7 预防:堆转储采集规范
- 建立版本管理:每个hprof文件命名包含 日期_应用版本_进程ID。
- 设置空间预警:自动dump前检查设备剩余空间 > 200MB + 预估堆大小。
- 定期清理旧文件:adb shell rm /sdcard/*.hprof 或使用Android自带的存储管理。
- 配置自动上传:在WIFI环境下自动将压缩后的hprof上传至分析服务器,减少人工操作。
推荐工具链:
- 自动化采集:
- 日志关联:同时抓取logcat到同一目录,便于对照时间点
- 批量转换:使用
- 云存储:将hprof.gz上传到S3/OSS,用MAT客户端直接解析
- 自动化采集:
adb shell am dumpheap + shell脚本- 日志关联:同时抓取logcat到同一目录,便于对照时间点
- 批量转换:使用
hprof-conv 配合 find -exec 批量转换- 云存储:将hprof.gz上传到S3/OSS,用MAT客户端直接解析