ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PSS/RSS废弃了?Android/Google主推memory cgroup(memcg)衡量和管理App内存?

2026/8/6 10:03:30 拓冰建站 浏览量
PSS/RSS废弃了?Android/Google主推memory cgroup(memcg)衡量和管理App内存? PSS/RSS废弃了Android/Google将用memory cgroup(memcg)衡量和管理App内存?摘要Android系统正逐渐转向使用memory cgroupmemcg作为应用内存管理的主要机制而非传统的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/v2PSS / 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 / memcgLinux cgroup 是一种资源隔离和统计机制。memory cgroup 用来统计和限制某个进程组的内存使用。Android 上每个 App 进程通常会被放进某个 cgroup例如uid_10123/pid_12345或者按 UID 聚合uid_10123这样系统就可以知道某个 App 或某个 UID 下面的所有进程一共占用了多少内存资源。memcg 不只是一个“统计工具”它还和系统内存回收、LMKD、OOM、内存压力管理等机制关系更近。2. PSS / RSS 分别是什么RSSResident Set SizeRSS 表示进程当前驻留在物理内存中的页面总量。可以理解为这个进程映射到的、当前在 RAM 里的内存页大小。查看方式adb shell cat /proc/pid/status | grep VmRSS或者adb shell cat /proc/pid/statm问题是RSS 对共享内存会重复计算。例如libandroid_runtime.so 被 100 个进程共享每个进程的 RSS 都可能把这部分算进去。所以如果把所有 App 的 RSS 加起来结果可能远远超过真实物理内存使用量。PSSProportional Set SizePSS 是 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 librarymmap 文件ashmemgraphics bufferART / 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 memoryfile cacheshmempage tablekernel stacksocket memoryslabswapzram 相关记账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先拿 pidadb shell pidof com.example.app例如输出12345然后看这个进程的 cgroupadb 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如果想看整个 UIDadb 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查看整个 UIDadb shell cat /sys/fs/cgroup/uid_10123/memory.current adb shell cat /sys/fs/cgroup/uid_10123/memory.stat9. 推荐的通用查看步骤可以按这个流程查。第一步找到 pidadb 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 v1adb 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 v2adb 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可以直接用 shelladb shell v$(cat /sys/fs/cgroup/uid_10123/pid_12345/memory.current); echo $((v/1024/1024)) MB11.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大致可以这样理解字段含义anon匿名内存例如 Java/Kotlin heap、native heap 等file文件页缓存、mmap 文件等shmemshared memorykernel内核侧记账内存部分系统有pagetables页表内存kernel_stack内核栈socksocket bufferactive_anon/inactive_anon活跃/非活跃匿名页active_file/inactive_file活跃/非活跃文件页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再看 memcgadb 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 等分类dumpsys meminfo看进程 PSS/RSS/SwapPSS/proc/pid/smaps_rollup看系统对 App/UID 的内存记账memcg看是否接近 LMK / 系统内存压力memcg lmkd log PSI看 Java 对象泄漏Android Studio Profiler / heap dump看 native 泄漏heapprofd / malloc debug / perfetto13. 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