内存限制器

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)。

修改配置

如需更改系统级限制,请按以下步骤操作:

  1. 修改 /vendor/etc/memory-limiter-config.xml
  2. 重新启动设备或重启 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
指示限制器是否正在主动监控进程。
visibleMemnotVisibleMem
指明每个状态的计算绝对内存限制。
events
进程超出其限制的次数。
processes
受监控的进程数。

ignore

ignore 子命令可暂时排除某个 UID 或所有进程,使其不受限制。此操作对于性能测试或允许特定应用超出其限制非常有用。

adb shell am memory-limiter ignore 10087  // Ignore a specific UID
adb 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 1234
adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234

手动替换仅适用于进程的生命周期。如果进程重启,则会根据其状态恢复为默认限制。