| 表头 | 含义 | 分析重点 |
|---|---|---|
| Class Name | 类名(对象的类型) | 找你自己的业务类、Activity/Fragment、Bitmap、大数组等 |
| Allocations | 该类的对象实例数量 | 数量异常多 → 可能重复创建或泄漏(比如 Activity 应该只有 1 个却有 5 个) |
| Native Size | 该类对象在Native 层(C/C++)占用的内存 | 图片、ByteBuffer、MediaCodec 等 Native 内存大户看这里 |
| Shallow Size | 对象自身占用的内存大小(不含它引用的对象) | 看单个对象本身的开销(比如 byte []、long [] 这种数组类) |
| Retained Size | 对象自身 + 它能直接 / 间接引用到的所有对象的总内存 | 最关键指标:代表如果这个对象被 GC 回收,能释放多少内存 |
内存大户:
-
byte[]:- Allocations:89,643 个
- Shallow Size:10,951,673 字节(约 10.4 MB)
- Native Size:99,804,707 字节(约 95.2 MB)→ 说明你的 App 里有大量字节数组,且很多关联到 Native 内存(大概率是图片、音视频、网络 IO 缓存)
-
DirectByteBuffer/DirectByteBuffer$MemoryRef:- Allocations:33,740 + 22,500 个
- Shallow Size:2,024,400 + 675,000 字节→ 大量直接内存缓冲区,常见于:
- 网络请求(OkHttp/Retrofit)
- 音视频编解码(MediaCodec)
- 图片加载库(Glide/Picasso 用的 Native Buffer)
-
MediaCodec$...系列:MediaCodec$BufferMap$CodecBuffer、MediaCodec$BufferInfo数量都在 22k+→ 明显是音视频播放 / 录制 / 推流场景在占用内存,可能是 Codec 资源没及时释放
三、下一步具体分析步骤(按优先级)
步骤 1:先确认 "谁在占内存最多"
- 点击
Shallow Size或Native Size列标题 ,按降序排序,锁定 Top 3~5 个大户(你这里已经是byte[]、String、DirectByteBuffer等) - 重点看:
- 你自己的业务类(包名开头的类,比如
com.xxx.xxx) Activity/Fragment:数量是否超过预期(比如关闭页面后还残留多个实例)Bitmap:如果存在,看 Shallow Size 和 Native Size(Bitmap 的 Native Size 就是图片像素内存)
- 你自己的业务类(包名开头的类,比如
步骤 2:深入分析 byte[] 这个最大户(核心操作)
- 双击
byte[]这一行,展开它的实例列表 - 选中一个大尺寸的 byte [] 实例(按 Shallow Size 排序,找最大的几个)
- 右侧面板切换到
References(引用链),看:- 谁在引用这个 byte []?
- 是你的业务代码?还是第三方库(Glide、OkHttp、FFmpeg、MediaCodec 等)?
- 引用路径里有没有 静态变量(static)、单例、长生命周期对象(比如 Application、Service) ?→ 如果是静态引用持有大 byte [],这就是典型的内存泄漏
步骤 3:分析 DirectByteBuffer 和 MediaCodec 相关对象
- 双击
DirectByteBuffer,看实例的引用链:- 来源:OkHttp 的响应缓存?图片加载的 Native Buffer?还是音视频流 Buffer?
- 数量是否随操作不断上涨(比如反复进入播放页面后数量翻倍)?
- 看
MediaCodec相关类:- 退出播放页面、停止推流后,这些对象数量是否下降?
- 如果不下降 → 说明 Codec 资源、Surface、Buffer 没释放(比如没调用
release()、stop())
步骤 4:判断是否存在内存泄漏
- 先点顶部的 垃圾桶图标(Force GC),触发垃圾回收
- 重新捕获一次堆转储(Dump Java Heap)
- 对比两次:
- 如果
byte[]、DirectByteBuffer、MediaCodec相关对象数量几乎没减少 → 泄漏风险极高 - 如果大幅减少 → 多半是临时缓存,属于正常波动
- 如果
步骤 5:定位到具体代码行
当你在引用链里找到可疑对象后:
- 右键引用链中的某一行 → 选择
Jump to Source(跳转到源码) - 直接定位到:
- 哪个类、哪一行代码创建了这个大对象
- 哪个地方持有了不该持有的引用(比如静态集合、单例)
四、针对你当前场景的针对性建议 💡
从你的堆转储看,明显偏向 Native/IO/ 音视频场景,建议重点排查:
- 图片加载 :
- 是否用了 Glide/Picasso?有没有正确配置
skipMemoryCache、diskCacheStrategy? - 是否有超大图没做采样压缩(
inSampleSize)?
- 是否用了 Glide/Picasso?有没有正确配置
- 音视频模块 :
- 播放 / 录制结束后,是否完整调用了
MediaCodec.release()、AudioTrack.release()等? - 是否复用了 Codec 而没有正确释放旧实例?
- 播放 / 录制结束后,是否完整调用了
- 网络 / IO 缓存 :
- OkHttp 是否配置了过大的连接池 / 缓存?
- 是否有读写大文件后没关闭流、没释放 Buffer?