Android 17 及更高版本包含内存限制器,这是一项使用 Linux cgroup v2 监控和限制应用进程内存用量的系统服务。内存限制器可防止单个应用消耗过多的系统内存,从而降低总体内存压力,并防止激进的低内存终止关键的前台进程和缓存进程。
机制
内存限制器与 Activity Manager 服务 (AMS) 集成,以跟踪进程生命周期事件和状态变化。内存限制器使用 Linux 内核 cgroup v2 层次结构强制执行内存限制。
为了支持内存限制器,设备内核必须启用 cgroup v2 和 memory 控制器。该服务特别依赖于以下属性:
memory.high- 软性限额。如果超出此值,相应进程会受到限制,并且内核会尝试主动从该进程回收内存。
memory.swap.max- 限制进程可使用的交换空间(例如 ZRAM)的最大量。
对应用的影响
在内存限制范围内运行的应用不受内存限制器的影响。
当应用超出其 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。
配置
在 Android 17 及更高版本中,内存限制器使用系统分区上 /system/etc/memory-limiter-config.xml 的标准平台配置文件进行配置。
XML 格式
配置文件遵循 memory-limiter-config.xsd 中定义的架构。该文件定义了多个按可用内存排序的限制集;服务会根据设备的可用 RAM 选择匹配的最高限制集。所有内存值均以兆比字节 (MiB) 为单位指定。
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM boundaries account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
<maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
<maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
<maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- 一个正整数,用于标识配置版本。此设置必须是
1。 minimumRequiredMemTotal应用此限制集所需的最低系统
MemTotal(以 MiB 为单位,包含下限)。该服务会根据内核在/proc/meminfo中报告的总可用内存来评估此值。了解内存预留和边界:内核
MemTotal值反映了在扣除硬件和固件内存预留后,操作系统可访问的物理 DRAM。预留内存会为 GPU、基带调制解调器、摄像头 ISP、安全执行环境和虚拟机监控程序等专用硬件组件预留 RAM,通常会占用 500 MiB 到 2 GiB 以上的物理内存。由于
MemTotal低于设备的标称物理 DRAM,因此minimumRequiredMemTotal阈值包含这些预留内存的边际值。 例如,以 16 GB 设备为目标的配置指定的下限为14336(14 GiB) 而不是16384(16 GiB),而以 12 GB 设备为目标的配置指定的下限为9216(9 GiB)。这样可确保每个设备都与其预期层级相符,无论板级划分有何变化。maximumAllowedMemTotal此限制集适用的最大系统
MemTotal(以 MiB 为单位,不含上限)。如果MemTotal大于或等于此上限,则设置的限制不适用。对于内存为 18,432 MiB 或更高的设备,或者内存低于 3,200 MiB 的设备,内存限制器不会应用限制,进程不受约束。memVisible应用于可见进程的软内存限制 (
memory.high),以 MiB 为单位。memNotVisible应用于不可见进程的软内存限制 (
memory.high),以 MiB 为单位。swapVisible应用于可见进程的交换空间限制 (
memory.swap.max),以 MiB 为单位。swapNotVisible应用于不可见进程的交换空间限制 (
memory.swap.max),以 MiB 为单位。
标准平台限制
在 Android 17 及更高版本中,平台在 /system/etc/memory-limiter-config.xml 中使用 [floor, ceiling) 范围间隔跨常见物理 RAM 层级指定标准内存和交换限制:
| 目标物理 RAM |
MemTotal 范围 [下限,上限) (minimumRequiredMemTotal, maximumAllowedMemTotal) |
可见内存限制 (memVisible) |
不可见的内存上限 (memNotVisible) |
可见交换限制 (swapVisible) |
不可见的换货限额 (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | [14336, 18432) MiB |
10,240 MiB (10 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) |
| 12 GB | [9216, 14336) MiB |
8,192 MiB (8 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) |
| 8 GB | [6800, 9216) MiB |
5,120 MiB (5 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) |
| 6 GB | [4800, 6800) MiB |
4,096 MiB (4 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) |
| 4 GB | [3200, 4800) MiB |
2,048 MiB (2 GiB) | 1,024 MiB (1 GiB) | 1,024 MiB (1 GiB) | 1,024 MiB (1 GiB) |
内存限制原则
内存限制器配置是围绕以下平台原则设计的:
生态系统一致性和应用兼容性:设备间一致的内存限制可确保整个 Android 生态系统中的应用性能可预测。应用是根据标准内存预期进行开发和测试的;保持统一的平台限制可避免意外的节流或过早终止。
按比例分配资源:平台限额根据物理 RAM 容量进行校准:
- 可见进程:分配了大约 1/2 到 2/3 的总物理 RAM 来支持活跃的界面、合成和渲染工作负载。
- 不可见进程:为后台任务分配了大约 1/4 到 1/3 的总物理 RAM。
普遍适用性:限制统一适用于设备上的所有应用进程 (UID >= 10000),包括预安装的系统应用和 OEM 应用。内存限制器不支持用于排除特定应用的许可名单,从而确保所有应用都能公平地使用内存。
缺少运行时查询 API:在 Android 17 及更高版本中,应用无法在运行时以编程方式查询其分配的内存限制。标准平台限制的定义留有充足的余量,以便行为良好的应用在正常使用期间不受限制地运行。
内核回收和交换动态:当应用进程接近其
memory.high限制时,Linux 内核会专门针对该进程 cgroup 指导内存回收。这包括舍弃非活跃的文件支持页面,以及将冷匿名页面交换到 ZRAM,从而在不施加全局内存压力的情况下保持系统流畅性。
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- 目前针对每种可见性状态强制执行的计算出的绝对内存限制 (
memory.high)。 visibleSwap和notVisibleSwap- 当前针对每种公开范围状态强制执行的计算出的绝对交换限制 (
memory.swap.max)。 events- 进程超出其分配限值的次数。
processes- 当前受监控的进程数。
ignore
ignore 子命令可暂时将特定 UID 或所有进程排除在内存限制之外。这对于性能基准化分析、压力测试或诊断内存行为非常有用:
# Ignore a specific UID adb shell am memory-limiter ignore 10087# Ignore all processes (temporarily disables limiting) adb shell am memory-limiter ignore all# Resume normal limiting operation adb shell am memory-limiter ignore none
手动
manual 子命令会通过进程 ID (PID) 以自定义绝对值(以字节为单位)替换特定进程的计算限制。该值必须是整数,但可以包含 MB 后缀(表示 MiB)或 GB 后缀(表示 GiB):
# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1073741824# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1024MB# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1GB# Remove the manual override for PID 1234 adb shell am memory-limiter manual 1234 none
手动替换仅在相应特定流程实例的生命周期内有效。 如果流程重新开始,则会根据其状态恢复为标准限额。
手动替换不能超过物理系统限制。