PSS/RSS废弃了?Android/Google将用memory cgroup(memcg)衡量和管理App内存?
摘要:Android系统正逐渐转向使用memory cgroup(memcg)作为应用内存管理的主要机制,而非传统的PSS/RSS指标。PSS/RSS仍用于调试(如dumpsys meminfo),但存在共享内存重复计算(RSS)、统计成本高(PSS)等问题,且无法准确反映系统内存回收收益。memcg通过内核级资源隔离和聚合统计(按UID/进程组),更贴近LMKD/OOM等系统管理逻辑,支持实时低开销监控。查看memcg数据需区分cgroup v1(/dev/memcg)或v2(/sys/fs/cgroup),结合memory.usage_in_bytes或memory.current等文件。建议内存分析时综合memcg、PSS及smaps_rollup数据,以全面评估应用内存占用。
关键词:memory cgroup, PSS/RSS, Android内存管理, LMKD, cgroup v1/v2
PSS / RSS 并不是完全“废弃不能用”了,它们仍然在 Android 调试中很常见,比如:
adb shell dumpsys meminfo <package> adb shell cat /proc/<pid>/status adb shell cat /proc/<pid>/smaps_rollup但在更底层、更接近系统内存治理的场景里,Android / Google 越来越倾向于用memory cgroup,简称 memcg来衡量和管理应用内存。
Linux Memory Control Group / memory cgroup。
1. 什么是 memory cgroup / memcg?
Linux cgroup 是一种资源隔离和统计机制。
memory cgroup 用来统计和限制某个进程组的内存使用。
Android 上每个 App 进程通常会被放进某个 cgroup,例如:
uid_10123/pid_12345或者按 UID 聚合:
uid_10123这样系统就可以知道:
某个 App 或某个 UID 下面的所有进程,一共占用了多少内存资源。
memcg 不只是一个“统计工具”,它还和系统内存回收、LMKD、OOM、内存压力管理等机制关系更近。
2. PSS / RSS 分别是什么?
RSS:Resident Set Size
RSS 表示进程当前驻留在物理内存中的页面总量。
可以理解为:
这个进程映射到的、当前在 RAM 里的内存页大小。
查看方式:
adb shell cat /proc/<pid>/status | grep VmRSS或者:
adb shell cat /proc/<pid>/statm问题是,RSS 对共享内存会重复计算。
例如:
libandroid_runtime.so 被 100 个进程共享每个进程的 RSS 都可能把这部分算进去。
所以如果把所有 App 的 RSS 加起来,结果可能远远超过真实物理内存使用量。
PSS:Proportional Set Size
PSS 是 Android 很长时间里常用的内存指标。
PSS 会把共享页面按比例分摊。
例如一个 100KB 的 so 页面被 10 个进程共享:
每个进程 PSS 只算 10KB所以 PSS 比 RSS 更适合回答:
这个进程大概应该为多少物理内存负责?
查看方式:
adb shell dumpsys meminfo <package>或者:
adb shell cat /proc/<pid>/smaps_rollup里面会有:
Pss: Rss: Private_Dirty: Private_Clean: SwapPss:3. 那为什么还要引入 memcg?
因为 PSS / RSS 都有一些天然问题,尤其是在现代 Android 系统中。
原因一:RSS 会严重重复计算共享内存
RSS 最大的问题是共享内存重复计算。
Android App 会共享很多东西:
zygote 预加载类;
framework 资源;
shared library;
mmap 文件;
ashmem;
graphics buffer;
ART / oat / vdex 映射;
WebView 相关共享资源。
如果只看 RSS,一个 App 可能看起来特别大,但里面很多其实是共享的。
所以 RSS 不适合作为 App 内存占用的标准指标。
原因二:PSS 计算成本比较高
PSS 需要遍历/proc/<pid>/smaps或内核相关页表统计。
这类统计相对昂贵。
单个进程看还好,如果系统频繁对所有进程算 PSS,就会有明显成本。
例如:
adb shell dumpsys meminfo这个命令本身就可能比较慢,因为它要收集很多进程的 smaps 信息。
在系统内存压力管理中,系统希望有一个更低成本、更实时、更适合内核使用的指标。
memcg 就更接近内核实时记账模型。
原因三:PSS 是“比例分摊模型”,但不一定符合真实回收/杀进程收益
PSS 的逻辑是:
共享内存按使用者数量均摊。
这在统计上比较公平,但对系统回收内存来说不一定准确。
举个例子:
某个共享页面被 A、B 两个进程使用。
PSS 中 A 算一半,B 算一半。
但如果杀掉 A,这个共享页面因为 B 还在用,可能并不会释放。
所以 PSS 回答的是:
这个进程应该分摊多少内存?
但系统内存治理更关心的是:
这个 App 进程组实际给系统带来了多少内存压力?
杀掉/回收这个 cgroup,大概能释放或缓解多少压力?
哪个 UID / App 正在消耗最多内存资源?
memcg 更接近系统调度、回收、LMK 的实际模型。
原因四:PSS/RSS 主要基于进程,但 Android App 不一定只有一个进程
一个 Android 应用可能有多个进程:
com.example.app com.example.app:remote com.example.app:webview com.example.app:push com.example.app:camera如果只看某个 pid 的 PSS/RSS,容易低估整个 App 的内存。
而 memcg 可以按 UID 或进程组聚合:
uid_10123这样更适合描述:
这个应用整体用了多少内存。
原因五:memcg 能覆盖更多内核视角的资源
PSS/RSS 更多是进程虚拟内存映射视角。
memcg 是内核 memory controller 的记账视角,现代内核里可以统计更多类型,例如:
anonymous memory;
file cache;
shmem;
page table;
kernel stack;
socket memory;
slab;
swap;
zram 相关记账;
cgroup 内聚合内存。
不同 Android 版本和内核配置统计范围会有差异,但总体上 memcg 更贴近内核真实内存压力管理。
4. 所以 PSS / RSS 为什么“不够用了”?
可以简单总结成:
指标 | 优点 | 问题 |
|---|---|---|
RSS | 获取简单,表示驻留物理内存 | 共享内存重复计算,容易虚高 |
PSS | 共享内存按比例分摊,更适合人工分析 | 计算成本高,不够实时,和系统回收收益不完全一致 |
memcg | 内核直接记账,适合按 App/UID 聚合,贴近 LMK/回收机制 | 解释起来不如 PSS 直观,不同系统版本路径和字段可能不同 |
所以不是 PSS/RSS “错了”,而是:
Android 系统级内存治理更需要一个低成本、实时、可聚合、和内核回收机制一致的指标。
这就是 memcg 越来越重要的原因。
5. memcg 和 PSS 的数值为什么可能不一样?
很正常。
因为它们的统计口径不同。
例如:
PSS = 进程地址空间中内存页的比例分摊 memcg = 内核 memory cgroup 对这个 cgroup 的实际记账 RSS = 当前进程 resident page 的总和常见差异来源包括:
共享页面计算方式不同;
page cache 是否计入;
kernel memory 是否计入;
swap/zram 是否计入;
是否按 UID 聚合;
多进程 App 是否全部计入;
图形 buffer / ashmem / dma-buf 的归属差异;
Android 版本和内核版本差异。
所以可能看到:
PSS = 300MB memcg = 420MB RSS = 800MB这不一定矛盾,只是统计口径不同。
6. Android 上怎么查看 memcg?
不同 Android 版本路径不同。最稳妥的方法是先看目标进程属于哪个 cgroup。
假设包名是:
com.example.app先拿 pid:
adb shell pidof com.example.app例如输出:
12345然后看这个进程的 cgroup:
adb shell cat /proc/12345/cgroup可能看到两类情况。
7. 情况一:cgroup v1,常见路径/dev/memcg
有些设备上会看到类似:
4:memory:/apps/uid_10123/pid_12345或者:
memory:/apps/uid_10123/pid_12345这表示 memory controller 的路径是:
/apps/uid_10123/pid_12345对应到文件系统可能是:
/dev/memcg/apps/uid_10123/pid_12345可以查看:
adb shell cat /dev/memcg/apps/uid_10123/pid_12345/memory.usage_in_bytes这个值单位是 byte。
也可以查看详细统计:
adb shell cat /dev/memcg/apps/uid_10123/pid_12345/memory.stat如果想看整个 UID:
adb shell cat /dev/memcg/apps/uid_10123/memory.usage_in_bytes adb shell cat /dev/memcg/apps/uid_10123/memory.stat8. 情况二:cgroup v2,常见路径/sys/fs/cgroup
新系统上可能看到:
0::/uid_10123/pid_12345这通常表示 unified cgroup v2。
对应路径一般类似:
/sys/fs/cgroup/uid_10123/pid_12345查看当前内存:
adb shell cat /sys/fs/cgroup/uid_10123/pid_12345/memory.current查看峰值,部分系统支持:
adb shell cat /sys/fs/cgroup/uid_10123/pid_12345/memory.peak查看详细统计:
adb shell cat /sys/fs/cgroup/uid_10123/pid_12345/memory.stat查看整个 UID:
adb shell cat /sys/fs/cgroup/uid_10123/memory.current adb shell cat /sys/fs/cgroup/uid_10123/memory.stat9. 推荐的通用查看步骤
可以按这个流程查。
第一步:找到 pid
adb shell pidof -s com.example.app假设得到:
12345第二步:查看 cgroup 信息
adb shell cat /proc/12345/cgroup输出如果类似:
4:memory:/apps/uid_10123/pid_12345说明大概率是 cgroup v1 memory。
如果类似:
0::/uid_10123/pid_12345说明大概率是 cgroup v2。
第三步:查看挂载点
adb shell cat /proc/mounts | grep cgroup或者:
adb shell cat /proc/mounts | grep memcg如果看到:
/dev/memcg cgroup ...走/dev/memcg。
如果看到:
/sys/fs/cgroup cgroup2 ...走/sys/fs/cgroup。
第四步:读取内存值
cgroup v1:
adb shell cat /dev/memcg/apps/uid_10123/pid_12345/memory.usage_in_bytes adb shell cat /dev/memcg/apps/uid_10123/pid_12345/memory.statcgroup v2:
adb shell cat /sys/fs/cgroup/uid_10123/pid_12345/memory.current adb shell cat /sys/fs/cgroup/uid_10123/pid_12345/memory.stat10. 如何把 byte 转成 MB?
比如:
adb shell cat /sys/fs/cgroup/uid_10123/pid_12345/memory.current输出:
268435456换算:
268435456 / 1024 / 1024 = 256 MB可以直接用 shell:
adb shell 'v=$(cat /sys/fs/cgroup/uid_10123/pid_12345/memory.current); echo $((v/1024/1024)) MB'11.memory.stat里面常见字段怎么看?
cgroup v2 的memory.stat常见字段可能有:
anon file kernel kernel_stack pagetables percpu sock shmem file_mapped file_dirty file_writeback swapcached anon_thp file_thp shmem_thp inactive_anon active_anon inactive_file active_file slab_reclaimable slab_unreclaimable大致可以这样理解:
字段 | 含义 |
|---|---|
| 匿名内存,例如 Java/Kotlin heap、native heap 等 |
| 文件页缓存、mmap 文件等 |
| shared memory |
| 内核侧记账内存,部分系统有 |
| 页表内存 |
| 内核栈 |
| socket buffer |
/
| 活跃/非活跃匿名页 |
/
| 活跃/非活跃文件页 |
cgroup v1 的memory.stat字段可能是:
cache rss rss_huge shmem mapped_file dirty writeback swap pgpgin pgpgout total_cache total_rss total_shmem total_mapped_file字段名字和含义会随内核版本、Android 版本变化。
12. 和dumpsys meminfo对比怎么看?
可以同时看:
adb shell dumpsys meminfo com.example.app以及:
adb shell cat /proc/<pid>/smaps_rollup再看 memcg:
adb shell cat /sys/fs/cgroup/uid_xxxxx/pid_xxxxx/memory.current或者:
adb shell cat /dev/memcg/apps/uid_xxxxx/pid_xxxxx/memory.usage_in_bytes会发现它们通常不会完全一致。
这是正常的。
建议这样使用:
场景 | 建议看什么 |
|---|---|
分析 Java heap / Native heap / Graphics / Code 等分类 |
|
看进程 PSS/RSS/SwapPSS |
|
看系统对 App/UID 的内存记账 | memcg |
看是否接近 LMK / 系统内存压力 | memcg + lmkd log + PSI |
看 Java 对象泄漏 | Android Studio Profiler / heap dump |
看 native 泄漏 | heapprofd / malloc debug / perfetto |
13. memcg 能否替代 PSS?
不能简单说完全替代。
更准确说:
memcg 更适合系统级内存治理和 App 整体内存归因;PSS 更适合传统应用内存分析和跨进程共享内存的比例分摊视角。
比如要回答:
App 为什么被 LMK 杀了?
应该重点看:
memcg usage PSI lmkd log oom_score_adj 系统 available memory而不是只看 PSS。
但要回答:
我的 Activity 页面打开后多占了多少 Java heap / native heap / graphics?
那dumpsys meminfo和 PSS 分类仍然很有价值。
14. 为什么 Google / Android 会更重视 memcg?
核心原因可以概括为一句话:
Android 的内存压力、回收、杀进程决策越来越依赖内核 cgroup 体系,memcg 是更贴近系统实际资源管理的口径。
具体包括:
可以按 UID / App 聚合;
可以低成本读取;
和 LMKD、OOM、内存压力机制更一致;
更适合多进程 App;
更适合现代 Android 的图形、WebView、zygote、mmap、zram 场景;
不需要频繁扫描 smaps;
可以和 PSI、cgroup reclaim 等机制联动。
15. 一个实际排查建议
如果在分析某个 App 的“真实内存占用”,建议同时采集这几类数据:
# 1. pid adb shell pidof com.example.app # 2. meminfo adb shell dumpsys meminfo com.example.app # 3. smaps_rollup adb shell cat /proc/<pid>/smaps_rollup # 4. cgroup adb shell cat /proc/<pid>/cgroup # 5. memcg usage/stat adb shell cat /sys/fs/cgroup/uid_xxxxx/pid_xxxxx/memory.current adb shell cat /sys/fs/cgroup/uid_xxxxx/pid_xxxxx/memory.stat如果是 cgroup v1,则换成:
adb shell cat /dev/memcg/apps/uid_xxxxx/pid_xxxxx/memory.usage_in_bytes adb shell cat /dev/memcg/apps/uid_xxxxx/pid_xxxxx/memory.stat总结
PSS / RSS 不是完全废弃,而是它们在现代 Android 系统内存治理中有局限:
RSS 对共享内存重复计算;
PSS 计算成本高;
PSS 是比例分摊模型,不完全等价于系统回收收益;
多进程 App 下单 pid PSS/RSS 容易低估整体;
它们和 LMKD / 内核回收 / cgroup 机制不完全一致。
memcg 的优势是:
内核直接记账;
可按进程组/UID/App 聚合;
更低成本;
更接近系统内存压力和 LMK 决策;
更符合现代 Android 的资源治理模型。
查看方式主要是:
adb shell cat /proc/<pid>/cgroup然后根据系统是 cgroup v1 还是 v2,读取:
/dev/memcg/.../memory.usage_in_bytes或:
/sys/fs/cgroup/.../memory.current