第1章 内存溢出故障树构建
1.1 现象识别
多媒体应用出现 OOM 时,通常表现为:
- 应用无响应(ANR)后直接闪退;
- Logcat 中出现
OutOfMemoryError或java.lang.OutOfMemoryError; - 系统日志提示
lowmemorykiller杀进程; - 图形渲染卡顿、纹理缺失或界面黑屏。
排查起点:一旦捕获上述现象,立即保存完整的
logcat 日志和 hprof 堆转储文件,避免重启丢失现场。
1.2 数据采集
采集三类数据对应三大内存区域:
| 内存区域 | 采集工具 / 命令 | 关键数据 |
|---|---|---|
| Java 堆 | adb shell am dumpheap <pid> /data/local/tmp/heap.hprof |
对象实例、GC 根路径、大对象数组 |
| Native 堆 | adb shell dumpsys meminfo <pid> + malloc debug |
Native 分配总量、backtrace、未释放指针 |
| 图形内存 | adb shell dumpsys gfxinfo | dumpsys SurfaceFlinger |
图形缓冲区大小、纹理数量、显存占用 |
1.3 原因定位 —— 故障树
OOM 故障树的根节点为 内存溢出,三个主支分别是 Java 堆、Native 堆、图形内存。每一支向下延伸出叶子判断条件,形成快速诊断路径。
故障树排查原则:从根向下逐层验证,先确认溢出区域,再定位具体对象/调用链,避免盲目优化。
下表列出各支路的关键判断条件:
| 支路 | 典型现象 | 采集手段 | 叶子判断条件 |
|---|---|---|---|
| Java 堆溢出 | 应用崩溃,logcat 直接打印 OutOfMemoryError |
hprof 堆转储 + MAT 分析 |
|
| Native 堆溢出 | 应用无 Java OOM 日志,但进程内存持续上涨直至被杀 | malloc debug / address sanitizer |
|
| 图形内存溢出 | 界面花屏、滑动卡顿、SurfaceFlinger 报 buffer 不足 | dumpsys SurfaceFlinger / gfxinfo |
|
1.4 修复动作
基于故障树定位到具体叶子后,修复方向如下:
- Java 堆:修复内存泄漏、减少 Bitmap 尺寸、使用对象池;
- Native 堆:检查第三方库资源释放、限制单次分配大小、改用 mmap 预申请;
- 图形内存:复用 GraphicBuffer、降低纹理分辨率、回收未使用的 Surface。
警告:不要在未确认故障树支路的情况下盲目修改堆大小参数(如
-Xmx),可能掩盖深层泄漏。
1.5 复测与验证
- 使用原触发场景重复操作,观察是否不再出现 OOM;
- 对比修复前后的
dumpsys meminfo总内存曲线; - 运行
monkey测试 30 分钟,确认无崩溃。
1.6 预防措施
| 预防维度 | 具体做法 |
|---|---|
| 监控 | 集成 Profiler 或 MemoryAnalyzer 定期采集堆转储 |
| 代码审查 | 禁止在 UI 线程分配大对象,必须使用异步解码 |
| 自动化测试 | CI 流水线中加入内存泄漏检测(LeakCanary) |
| 限制资源 | 图形内存设置纹理池大小上限,超出则降级 |