3. Android进程与内存模型

Android 的内存回收并不是“谁占得多就杀谁”,而是围绕“进程对用户有多重要”建立的一套分级模型。理解这套模型,是后续理解 LMKD(Low Memory Killer Daemon)调优的前提。本节从进程优先级出发,串联 oom_adj / oom_score_adj、进程生命周期,以及内存分组与 cgroup 基础,帮助你建立一张完整的“内存压力下的进程处置地图”。

一、核心原理:优先级决定“谁先被牺牲”

Android 在 Linux 进程模型之上,叠加了一套面向用户体验的重要性分级。系统会根据进程当前承载的组件(前台 Activity、可见界面、后台服务、缓存等)动态调整其优先级。当内存紧张时,内核与 LMKD 依据这个优先级决定回收顺序:越不重要的进程越先被回收,越接近用户当前操作的进程越晚被回收。

这里有两个容易混淆的参数:

  • oom_adj:早期 Android 使用的旧接口,取值范围约为 -17 到 15,数值越小越重要。它由框架层(ActivityManagerService)计算并写入。
  • oom_score_adj:Linux 内核主线采用的接口,取值范围为 -1000 到 1000,数值越小越重要。现代 Android 已统一使用它,oom_adj 更多作为历史概念存在。

内核的 OOM 评分机制会把进程实际内存占用与 oom_score_adj 结合,得到一个综合分数。分数越高,越容易被选中回收。因此 oom_score_adj 本质上是“人为施加的偏好权重”,而不是内存大小的直接度量。

关键理解:oom_score_adj 不是“内存占用”,而是“被回收的倾向”。一个占用很小但 adj 很高的进程,可能比一个占用很大但 adj 很低的进程更早被杀。

二、进程生命周期与优先级迁移

一个 Android 应用进程会随着用户操作在多个状态间迁移,其 oom_score_adj 也随之变化。典型路径如下:

  1. 前台进程(Foreground):用户正在交互,adj 最低,几乎不会被回收。
  2. 可见进程(Visible):界面可见但不在前台,adj 较低。
  3. 前台服务进程(Foreground Service):通过通知等方式声明正在执行重要任务,adj 中等偏低。
  4. 后台进程(Background):用户已离开,adj 升高,进入可回收候选。
  5. 缓存进程(Cached):仅保留以便快速重启,adj 最高,是内存回收的首选目标。
  6. 空进程(Empty):无任何活跃组件,最容易被回收。

当内存压力上升时,LMKD 会从 adj 最高的层级开始逐级回收,直到压力缓解。这个过程是“分级递进”的,而不是一次性清空所有后台进程。

Android 进程优先级与内存回收分级模型 进程状态(重要性由高到低) 前台进程 Foreground 可见进程 Visible 前台服务 Foreground Service 后台进程 Background 缓存进程 Cached 空进程 Empty oom_score_adj 取值趋势 -1000(最重要) 1000(最易回收) ≈ -900 ≈ -700 ≈ -500 ≈ 200 ≈ 700 ≈ 900 内存压力下的回收顺序 ① 空进程 / 缓存进程 ② 后台进程 ③ 前台服务进程 ④ 可见进程 ⑤ 前台进程(最后) LMKD 依据 oom_score_adj 从高到低逐级回收,直到内存压力缓解;cgroup 用于对进程组进行内存限制与统计 adj 越小越重要,越晚被回收;adj 越大越容易被回收

三、内存分组与 cgroup 基础

仅有优先级还不够,系统还需要对进程进行“分组管理”,以便统一限制和统计内存。这正是 cgroup(control group)的作用。Android 使用 cgroup 将进程划分到不同的内存组中,常见的分组包括:

  • foreground 组:前台与可见进程,内存限制较宽松。
  • background 组:后台进程,限制更严格。
  • system 组:系统关键进程,通常受到保护。

通过 cgroup,系统可以对不同组设置内存上限、统计内存使用量,并在组内触发回收。LMKD 会结合 cgroup 的内存压力信息与进程的 oom_score_adj,做出更精细的回收决策。

# 查看进程的 oom_score_adj(示例)
adb shell cat /proc/<pid>/oom_score_adj

# 查看 cgroup 挂载与分组信息
adb shell cat /proc/<pid>/cgroup

# 查看 LMKD 相关属性
adb shell getprop | grep lmkd

四、案例场景:一次后台应用被回收的完整链路

假设用户正在使用浏览器,此时音乐应用退到后台继续播放。系统会经历如下过程:

  1. 音乐应用从前台变为后台,ActivityManagerService 将其 oom_score_adj 从较低值调高。
  2. 该进程被归入 background cgroup,内存统计与限制随之生效。
  3. 用户继续打开大型游戏,系统内存压力上升。
  4. LMKD 检测到内存低于阈值,从 adj 最高的缓存进程开始回收。
  5. 若压力持续,音乐应用(后台但仍在播放)可能被回收,除非它已通过前台服务提升优先级。
如果希望后台任务不被轻易回收,正确做法是使用前台服务并配合通知,而不是盲目调低 oom_score_adj。后者可能被系统策略覆盖,且不利于整体内存健康。

五、常见误区与复盘

误区 正确理解
oom_score_adj 越低,进程占用内存越少 它只表示回收倾向,与内存占用无直接关系
手动改 adj 就能永久保活 框架层会动态重算,且系统策略优先
缓存进程没有价值,可以随意杀 缓存进程用于加速重启,过度回收会拖慢体验
cgroup 只用于限制 CPU cgroup 同样用于内存分组、统计与回收

复盘时建议重点关注:进程当前处于哪个优先级层级、oom_score_adj 是否被异常修改、所属 cgroup 分组是否合理,以及 LMKD 的回收阈值是否与设备内存规格匹配。只有把“优先级—adj—cgroup—LMKD”这条链路打通,后续的调优才有稳固的落脚点。