Android 17 及更高版本支持内存限制器,这是一项系统服务,可使用 Linux cgroup v2 监控和限制应用进程的内存用量。内存限制器可防止单个应用消耗过多的系统内存,从而降低整个系统的内存压力,并防止激进地终止关键进程以解决内存不足 (OOM) 问题。
机制
内存限制器与 Activity 管理器服务 (AMS) 集成,以跟踪进程生命周期事件和状态变化。内存限制器使用 Linux 内核 cgroup v2 文件系统强制执行内存限制。
如需使用内存限制器,设备内核必须支持 cgroup v2 和 memory 控制器。该服务特别依赖于以下属性:
memory.high- 软性限额。如果超出此值,进程会被限制,并且内核会尝试从中回收内存。
memory.swap.max- 限制进程可使用的交换空间量。
对应用的影响
未超出内存限制的应用不受内存限制器的影响。
当应用超出其 memory.high 限制时,内核会逐出应用的文件支持内存并换出其匿名内存,以使应用保持在限制范围内。由于驱逐和交换,应用可能会运行得更慢。
在极端情况下,如果应用继续分配匿名内存,而设备用尽了交换空间,则应用可能无法分配内存,从而很可能会崩溃。
Process Monitoring
内存限制器默认监控应用进程(UID >= 10000)。系统进程通常不受此限制,以帮助验证核心系统稳定性。
内存限制器会根据进程的状态分配内存限制:
可见进程是用户可察觉到的进程,例如前台 activity、前台服务或其他用户可察觉到卡顿的状态。
不可见进程是指不与用户互动或对用户不可见的后台进程。
下表将特定进程状态映射到内存限制:
| 进程状态 | 内存上限 |
|---|---|
PERSISTENT | 无限制 |
PERSISTENT_UI | 无限制 |
TOP | 可见 |
BOUND_TOP | 可见 |
FOREGROUND_SERVICE | 不显示 |
BOUND_FOREGROUND_SERVICE | 不显示 |
IMPORTANT_FOREGROUND | 可见 |
IMPORTANT_BACKGROUND | 不显示 |
TRANSIENT_BACKGROUND | 不显示 |
BACKUP | 不显示 |
SERVICE | 不显示 |
RECEIVER | 不显示 |
TOP_SLEEPING | 可见 |
HEAVY_WEIGHT | 不显示 |
HOME | 不显示 |
LAST_ACTIVITY | 不显示 |
CACHED_ACTIVITY | 已缓存 |
CACHED_ACTIVITY_CLIENT | 已缓存 |
CACHED_RECENT | 已缓存 |
CACHED_EMPTY | 已缓存 |
在缓存状态下,进程会被冻结,然后尽可能地回收。
当进程超出其分配的 memory.high 限制时,内存限制器会检测到该事件,并可以触发调试操作,例如捕获内存配置文件或将异常记录到 statsd。
配置
使用位于 vendor 分区上的 XML 文件配置内存限制器。通过此配置,您可以根据设备的具体内存限制来调整绝对内存限制。
文件路径:
/vendor/etc/memory-limiter-config.xml默认配置:如果找不到配置文件,或者该文件无法读取或无效,则内存限制器会被停用。
XML 格式
配置文件遵循 memory-limiter-config.xsd 中定义的架构。该文件可让您定义多个限制集;服务会根据设备的可用 RAM 选择最匹配的限制集。所有内存值均以兆比字节 (MiB) 为单位进行定义。
<MemoryLimiterConfig>
<version>1</version>
<configList>
<limitSet>
<!-- Limits for a phone with at least 14G of ram: 8G/4G/4G/4G -->
<minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- 一个正整数,用于标识配置版本。此值必须为 1。
minimumRequiredMemTotal- 此限制集生效所需的最低可用系统内存。
memVisible- 可见进程允许的内存限制 (
memory.high)。 memNotVisible- 允许非可见进程使用的内存上限 (
memory.high)。 swapVisible- 可见进程允许的交换内存限制 (
memory.swap.max)。 swapNotVisible- 不可见进程允许的交换内存限制 (
memory.swap.max)。
修改配置
如需更改系统级限制,请按以下步骤操作:
- 修改
/vendor/etc/memory-limiter-config.xml。 - 重新启动设备或重启
system_server以使更改生效。
Shell 命令
借助 am memory-limiter 命令,您和开发者可以在运行时与服务互动,以进行开发和测试:
am memory-limiter <SUB-COMMAND>状态
status 子命令会报告内存限制器的运行状态:
adb shell am memory-limiter status输出示例:
Memory limiter
enabled monitoring=true ignored=none
visibleMem=1948MB visibleSwap=974MB
notVisibleMem=974MB notVisibleSwap=487MB
started=36 watched=36 watch-failed=0
events=0 processes=36 process-hwm=36
输出中的关键字段包括:
monitoring- 指示限制器是否正在主动监控进程。
visibleMem和notVisibleMem- 指明每个状态的计算绝对内存限制。
events- 进程超出其限制的次数。
processes- 受监控的进程数。
ignore
ignore 子命令可暂时排除某个 UID 或所有进程,使其不受限制。此操作对于性能测试或允许特定应用超出其限制非常有用。
adb shell am memory-limiter ignore 10087 // Ignore a specific UIDadb shell am memory-limiter ignore all // Ignore all processes (effectively disables limiting)adb shell am memory-limiter ignore none // Resume normal operation
手动
manual 子命令会使用自定义的绝对值(以兆字节 [MB] 为单位)替换特定进程(按进程 ID 或 PID)的计算限制:
adb shell am memory-limiter manual 1234 1024 // Set a 1024 MB limit for PID 1234adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234
手动替换仅适用于进程的生命周期。如果进程重启,则会根据其状态恢复为默认限制。