4. dumpsys meminfo详解:内存到底被谁吃了?
做性能优化这么多年,我几乎每天都要跟 dumpsys meminfo
别急,我带你把它掰开揉碎。这一章先吃透指标,再看进程分布,最后聊聊怎么从数据里揪出内存泄漏。
4.1 三个核心指标:PSS、RSS、VSS
这三个缩写,说白了就是系统从不同视角算的内存占用。
| 指标 | 全称 | 含义 | 特点 |
|---|---|---|---|
| VSS | Virtual Set Size | 进程申请的虚拟内存总量 | 包含共享库、已映射文件等,通常很大,参考价值低 |
| RSS | Resident Set Size | 进程实际常驻在物理内存中的大小 | 会重复计算共享库,导致总和超过实际物理内存 |
| PSS | Proportional Set Size | 按比例分摊共享库后的实际物理内存 | 最接近真实占用,各进程PSS之和≈总内存使用量 |
我的建议:日常优化只看PSS。RSS容易虚高,VSS更是「虚胖」。我在项目中遇到过一次,某App RSS显示500MB,但PSS只有180MB,结果发现是共享库重复计算导致的——差点冤枉了它。
4.2 数据怎么抓?
简单一条命令就能拿到全量信息:
adb shell dumpsys meminfo -a com.example.app
输出结果通常分成几大块:
- Total PSS by category – 按类别汇总
- Dalvik Heap – Java堆相关
- Native Heap – C/C++堆
- Stack & Code – 栈和代码段
- Graphics / GL – 图形内存
- Other dev – 其他设备映射
嗯,这里有个坑要注意——dumpsys meminfo 默认是汇总值,想看详细堆内部分布得加 -a 参数。我刚开始忘了加,盯着总数值看了半天没头绪。
4.3 Native Heap 与 Dalvik Heap 到底怎么分?
Android应用的内存其实分两大阵营:
Dalvik Heap(Java堆)
- 管理
new出来的Java对象 - 由GC自动回收
- PSS包括对象本身+GC管理开销
- 常见泄漏:Activity未释放、Bitmap对象、Handler引用
Native Heap(原生堆)
- 管理
malloc/new分配的C/C++内存 - 由开发者手动释放,或通过智能指针
- 包括:Bitmap像素数据、解码库缓冲区、OpenGL资源
- 泄漏通常更隐蔽,排查需要用
malloc debug或address sanitizer
4.4 各进程内存分布怎么看?
实际工作中,我们经常要对比多个进程的内存占用。 dumpsys meminfo 也支持看所有进程:
adb shell dumpsys meminfo
输出会按PSS从大到小排序。你一眼就能看到哪个进程是「内存大户」。我曾经用它抓过一个系统级预装App,在后台偷偷缓存图片,PSS长期保持150MB+。
输出表格里每行格式类似:
PID Pss Uss Rss Name
12345 180MB 120MB 350MB com.example.app
12346 80MB 60MB 120MB com.android.phone
...
重点关注 Pss 和 Uss(Unique Set Size,独占内存)。如果Uss占比高,说明这个App独立占用了大量物理内存,泄漏风险更大。
4.5 内存泄漏的线索藏在哪儿?
内存泄漏不会直接告诉你说「我泄漏了」,但数据会出卖它。我总结了几条常见特征:
- Dalvik Heap 持续增长且GC后不回落 – Java对象泄漏,典型如Activity引用链未断。
- Native Heap 增长但Java堆稳定 – C/C++泄漏,常见于图片加载库、编解码器。
- Graphics / GL 内存只增不减 – 纹理或Surface未释放,OpenGL相关。
- 某个进程的PSS随时间单调递增 – 运行监测3~5次,每次增加10MB以上,基本就是泄漏。
4.6 快速定位泄漏的实操流程
分享一下我的标准打法:
- 抓基线 – 启动App,等几秒,跑一次
dumpsys meminfo -a,记录各区域PSS。 - 执行可疑操作 – 比如反复进出某个Activity、下载大图、播放视频。
- 抓第二次 – 操作后等几秒再跑一次,查看变化。
- 对比关键区域 – 重点关注 Dalvik Heap 和 Native Heap 的
Pss Total差值。 - 多次采样 – 重复步骤2-4三次以上,如果每次PSS都增加且不回落,基本实锤泄漏。
4.7 实战小贴士:怎么用Systrace联合看?
光靠 dumpsys meminfo 还不够,因为它只能拍到快照。真正的卡顿发生在时间线上。我通常这样配合:
- Systrace 里加内存跟踪 – 开启
-a com.example.app和--memtrack,可以看到Java堆和Native堆随时间变化。 - 在关键操作前后打tag – 比如
Trace.beginSection("enter_detail"),结合meminfo快照,就能定位是哪段代码导致内存飙升。
gfx、input、view 和 dalvik。如果某帧掉帧时,Dalvik Heap刚好出现一个陡增,八成是对象分配过多。这时候再切到 dumpsys meminfo 确认具体区域,双管齐下效率很高。
好了,这一章的内容基本讲透。PSS/RSS/VSS的差异、两种堆的特性、以及从数据里找泄漏的套路,都是我在项目里反复验证过的。你拿到一份meminfo输出,至少知道该先看哪个数字,哪里可疑。下回遇到内存问题,不妨先按这套思路走一遍。