Android 平台在运行时不会浪费可用的内存。它会一直尝试利用所有可用内存。例如,系统会在应用关闭后将其保留在内存中,以便用户快速切回到这些应用。因此,通常情况下,Android 设备在运行时几乎没有可用的内存。若要在重要系统进程和许多用户应用之间正确分配内存,内存管理至关重要。
本页讨论了 Android 如何为系统和用户应用分配内存的基础知识,另外还说明了操作系统如何应对低内存情况。
Android 设备包含三种不同类型的内存:RAM、zRAM 和存储器。请注意,CPU 和 GPU 访问同一个 RAM。

图 1. 内存类型 - RAM、zRAM 和存储器
RAM 分为多个“页面”。通常,每个页面为 4KB 的内存。
系统会将RAM页面视为“可用”或“已使用”。
可用页面是未使用的 RAM。
已使用的页面是系统目前正在使用的 RAM,并分为以下类别:
缓存页:有存储器中的文件(例如代码或内存映射文件)支持的内存。
匿名页:没有存储器中的文件支持的内存(例如,由设置了 MAP_ANONYMOUS 标志的 mmap() 进行分配)
干净页包含存在于存储器中的文件(或文件一部分)的精确副本。
干净页可以删除,因为始终可以使用存储器中的数据重新生成它们;
脏页不能删除,否则数据将会丢失。
随着系统积极管理 RAM,可用和已使用页面的比例会不断变化。
本部分介绍的概念对于管理内存不足的情况至关重要。
本文档的下一部分将对这些概念进行更详细的说明。
内核交换守护进程 (kswapd:The Kernel Swap Daemon (kswapd)) 是 Linux 内核的一部分,用于将已使用内存转换为可用内存。
当设备上的可用内存不足时,该守护进程将变为活动状态。
Linux 内核设有可用内存上下限阈值(yùzhí)。
kswapd 开始回收内存。kswapd 停止回收内存。kswapd 可以删除干净页来回收它们,因为这些页受到存储器的支持且未经修改。
如果某个进程尝试处理已删除的干净页,系统会将该页面从存储器复制到 RAM。
此操作称为“请求分页”。

kswapd 可以将缓存的私有脏页和匿名脏页移动到 zRAM 进行压缩。
如果可用内存量低于特定阈值,系统会开始终止进程。

图 3. 脏页被移至 zRAM 并进行压缩
kswapd 不能为系统释放足够的内存。
onTrimMemory() 通知应用内存不足,应该减少其分配量。oom_adj_score 的“内存不足”分值来确定正在运行的进程的优先级,以此决定要终止的进程。

以下是上表中各种类别的说明:
oom_adj_score 的应用开始终止后台应用。kswapd)。设备制造商可以更改 LMK 的行为。
内核会跟踪系统中的所有内存页面。

图 5. 不同进程使用的页面
在确定应用使用的内存量时,系统必须考虑共享的页面。

如需确定应用的内存占用量,可以使用以下任一指标:
常驻内存大小 (RSS):应用使用的共享和非共享页面的数量
按比例分摊的内存大小 (PSS):应用使用的非共享页面的数量加上共享页面的均匀分摊数量
独占内存大小 (USS):应用使用的非共享页面数量(不包括共享页面)
如果操作系统想要知道所有进程使用了多少内存,那么 PSS 非常有用,因为页面只会统计一次。
cgroup 提供一种机制,可将任务集(包括进程、线程及其所有未来的子级)聚合并分区到具有专门行为的层级组中。Android 使用 cgroup 控制及考量 CPU 和内存等系统资源的使用和分配情况,并支持 Linux 内核 cgroup v1 和 cgroup v2。
lmkd) 进程可监控运行中的 Android 系统的内存状态,并通过终止最不必要的进程来应对内存压力大的问题,使系统以可接受的性能水平运行。lmkd 来执行内存监控和进程终止任务。lmkd 模式,它使用内核压力失速信息 (PSI) 监视器来检测内存压力。
lmkd)指定这些延迟的阈值,并在突破阈值时从内核订阅事件。vmpressure 信号(由内核生成,用于检测内存压力并由 lmkd 使用)通常包含大量误报,
lmkd 必须执行过滤以确定是否真的存在内存压力。lmkd 唤醒并使用额外的计算资源。PSI 监视器可以实现更精确的内存压力检测,并最大限度地减少过滤开销。vmpressure 事件),请配置 ro.lmk.use_psi 属性。
true,这会以 PSI 监视器作为 lmkd 内存压力检测的默认机制。CONFIG_PSI=y) 的情况下进行编译。由于存在大量问题,Android 弃用了 LMK 驱动程序,问题包括:
vmscan 进程变慢。lmkd 可实现与内核中的驱动程序相同的功能,但它使用现有的内核机制检测和评估内存压力。vmpressure 事件或压力失速信息 (PSI) 监视器来获取关于内存压力水平的通知,以及使用内存 cgroup 功能限制根据进程的重要性分配给每个进程的内存资源。lmkd 会在未检测到内核中的 LMK 驱动程序时激活。lmkd 要求内核支持内存 cgroup,因此必须使用以下配置设置编译内核:CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
用户空间 lmkd 支持基于以下各项的终止策略:
vmpressure 事件或 PSI 监视器、其严重性以及交换利用率等其他提示。低内存设备和高性能设备的终止策略有所不同:
对于内存不足的设备,一般情况下,系统会选择承受较大的内存压力。
对于高性能设备,如果出现内存压力,则会视为异常情况,应及时修复,以免影响整体性能。
您可以使用 ro.config.low_ram 属性配置终止策略。如需了解详情,请参阅低 RAM 配置。
用户空间 lmkd 还支持一种旧模式,在该模式下,它使用与内核中的 LMK 驱动程序相同的策略(即可用内存和文件缓存阈值)做出终止决策。要启用旧模式,请将 ro.lmk.use_minfree_levels 属性设置为 true。
使用以下属性为特定设备配置 lmkd。
| 属性 | 使用 | 默认 |
|---|---|---|
ro.config.low_ram | 指定设备是低内存设备还是高性能设备。 | false |
ro.lmk.use_psi | 使用 PSI 监视器(而不是 vmpressure 事件)。 | true |
ro.lmk.use_minfree_levels | 使用可用内存和文件缓存阈值来做出进程终止决策(即与内核中的 LMK 驱动程序的功能一致)。 | false |
ro.lmk.low | 在低 vmpressure 水平下可被终止的进程的最低 oom_adj 得分。 | 1001 (停用) |
ro.lmk.medium | 在中等 vmpressure 水平下可被终止的进程的最低 oom_adj 得分。 | 800 (已缓存或非必要服务) |
ro.lmk.critical | 在临界 vmpressure 水平下可被终止的进程的最低 oom_adj 得分。 | 0 (任何进程) |
ro.lmk.critical_upgrade | 支持升级到临界水平。 | false |
ro.lmk.upgrade_pressure | 由于系统交换次数过多,将在该水平执行水平升级的 mem_pressure 上限。 | 100 (停用) |
ro.lmk.downgrade_pressure | 由于仍有足够的可用内存,将在该水平忽略 vmpressure 事件的 mem_pressure 下限。 | 100 (停用) |
ro.lmk.kill_heaviest_task | 终止符合条件的最繁重任务(最佳决策)与终止符合条件的任何任务(快速决策)。 | true |
ro.lmk.kill_timeout_ms | 从某次终止后到其他终止完成之前的持续时间(以毫秒为单位)。 | 0 (停用) |
ro.lmk.debug | 启用 lmkd 调试日志。 | false |
注意:mem_pressure = 内存使用量/RAM_and_swap 使用量(以百分比的形式表示)。
设备配置示例:
PRODUCT_PROPERTY_OVERRIDES += \
ro.lmk.low=1001 \
ro.lmk.medium=800 \
ro.lmk.critical=0 \
ro.lmk.critical_upgrade=false \
ro.lmk.upgrade_pressure=100 \
ro.lmk.downgrade_pressure=100 \
ro.lmk.kill_heaviest_task=true
lmkd。lmkd 会根据内存资源使用情况和抖动来防止出现内存不足和性能下降。对于 Android 11 设备,lmkd 需要以下内核功能:
必须使用以下配置设置编译内核:
CONFIG_PSI=y
Android 11 中的内存终止策略支持下面列出的调节旋钮和默认值。这些功能在高性能设备和低内存设备上都可使用。
| 属性 | 使用 | 默认 | |
|---|---|---|---|
| 高性能 | 低内存 | ||
ro.lmk.psi_partial_stall_ms | 部分 PSI 失速阈值(以毫秒为单位),用于触发内存不足通知。如果设备收到内存压力通知的时间太晚,可以降低此值以在较早的时间触发通知。如果在不必要的情况下触发了内存压力通知,请提高此值以降低设备对噪声的敏感度。 | 70 | 200 |
ro.lmk.psi_complete_stall_ms | 完全 PSI 失速阈值(以毫秒为单位),用于触发关键内存通知。如果设备收到关键内存压力通知的时间太晚,可以降低该值以在较早的时间触发通知。如果在不必要的情况下触发了关键内存压力通知,可以提高该值以降低设备对噪声的敏感度。 | 700 | |
ro.lmk.thrashing_limit | 工作集 refault 数量的上限,以占具有文件支持的页面缓存总大小的百分比表示。如果工作集 refault 的数量超过该值,则视为系统对其页面缓存造成抖动。如果设备性能在内存压力期间受到影响,请降低该值以限制抖动。如果因抖动原因而导致设备性能不必要地降低,请提高该值以允许更多抖动。 | 100 | 30 |
ro.lmk.thrashing_limit_decay | 抖动阈值衰减,以占在系统无法恢复时(甚至是终止后)用于降低阈值的原始阈值的百分比表示。如果持续抖动导致不必要的终止,请降低该值。如果终止后对持续抖动的响应速度过慢,请提高该值。 | 10 | 50 |
ro.lmk.swap_util_max | 最大交换内存量,以占可交换内存总量的百分比表示。如果交换的内存量超过此上限,则表示系统在交换了其大部分可交换内存后仍然存在压力。 当内存压力是由不可交换内存的分配导致时,就可能会发生这种情况,原因在于大部分可交换内存已经交换,所以无法通过交换来缓解这一压力。默认值为 100,这实际上会停用此检查。如果设备的性能在交换利用率较高且可用交换水平未降至 ro.lmk.swap_free_low_percentage 的内存压力期间受到影响,请降低该值以限制交换利用率。 | 100 | 100 |
以下旧的调节旋钮也可用于新的终止策略。
| 属性 | 使用 | 默认 | |
|---|---|---|---|
| 高性能 | 低内存 | ||
ro.lmk.swap_free_low_percentage | 可用交换水平,以占总交换空间的百分比表示。“lmkd”使用该值作为阈值来判断何时将系统视为交换空间不足。如果“lmkd”因交换空间过多而终止,请降低该百分比。如果“lmkd”终止得太晚,从而导致 OOM 终止,请提高该百分比。 | 20 | 10 |
ro.lmk.debug | 这会启用“lmkd”调试日志。在调节时启用调试。 | false |