Android 的进程模型是其系统架构的核心设计之一,它基于 Linux 进程模型,但结合了 Android 组件化特性和内存管理需求,形成了一套"动态生命周期"和"优先级驱动"的进程管理机制。对于系统开发工程师,需要从以下几个维度理解其设计本质:
1. 基础:基于 Linux 进程,但受 AMS 管控
Android 应用进程本质是 Linux 进程(由 Zygote 进程 fork 生成,共享虚拟机和框架代码),拥有独立的 PID、UID(应用签名决定,用于权限隔离),但进程的创建、存活、销毁并非完全由应用自主控制,而是由 ActivityManagerService(AMS) 主导调度:
- 进程首次启动时,AMS 通过 Zygote 进程 fork 新进程(避免重复初始化虚拟机,提升启动速度)。
- 进程的生命周期与组件状态强绑定(如 Activity 前台/后台状态会影响进程优先级)。
- 系统内存不足时,AMS 会按优先级主动终止低优先级进程,释放资源。
2. 核心:进程优先级(OOM 调整值)
Android 进程的存活优先级由 OOM 调整值(oom_adj) 决定,该值由 AMS 动态计算并通过 Process.setOomAdj()
作用于内核,oom_adj
越小,优先级越高(越难被杀死)。主要优先级从高到低分为以下几类(以 Android 12+ 为例):
优先级类别 | 典型场景 | oom_adj 范围(示例) | 说明 |
---|---|---|---|
前台进程 | 正在交互的 Activity(onResume() 状态)、绑定到前台 Activity 的 Service |
-1000 ~ -200 | 优先级最高,只有在系统内存极度不足时才可能被杀死(几乎不杀)。 |
可见进程 | 处于 onPause() 状态的 Activity(如被对话框遮挡)、提供可见 UI 的 Service |
-199 ~ -100 | 可见但非交互,优先级仅次于前台进程,一般不被杀死。 |
服务进程 | 已启动的后台 Service(startService() )且未绑定前台组件 |
50 ~ 100 | 优先级高于后台进程,系统会尽量保留(如音乐播放 Service)。 |
后台进程 | 所有 Activity 都处于 onStop() 状态(用户看不到,但未销毁) |
100 ~ 200 | 优先级较低,内存不足时可能被批量回收,回收后用户返回需重新创建 Activity。 |
空进程 | 无任何活跃组件(所有组件已销毁,但进程未退出) | 200+ | 仅作为缓存(加速下次启动),内存不足时优先被杀死。 |
动态调整逻辑 :
AMS 通过 ProcessList
实时计算 oom_adj
,例如:
- 当用户切换应用时,前台进程变为后台进程,
oom_adj
升高。 - 后台 Service 调用
startForeground()
可提升为前台服务进程,oom_adj
降低。 - 进程持有
SYSTEM_ALERT_WINDOW
权限显示悬浮窗时,可能被归类为可见进程。
3. 进程与组件的绑定关系
进程的优先级由其内部最活跃的组件决定("取最高优先级"原则):
- 一个进程中若同时有前台 Activity 和后台 Service,进程优先级按前台 Activity 计算。
- 若进程中只有已停止的 Activity 和一个后台 Service,优先级按 Service 计算(高于纯后台进程)。
- 当所有组件都销毁后,进程变为空进程,等待被回收或下次复用。
4. 进程间通信(IPC)与隔离
- 隔离性:不同 UID 的进程默认无法直接访问彼此的内存(Linux 权限机制),需通过 Binder 机制进行 IPC。
- 共享进程 :同一 UID 的应用(如通过
sharedUserId
配置)可运行在同一进程(需指定相同process
名),共享内存空间。 - 系统进程 :
system_server
进程(运行 AMS、WMS 等核心服务)、zygote
进程、lmkd
(低内存杀手)等系统进程拥有高权限(UID=0 或系统 UID),不受普通应用进程的 OOM 规则限制。
5. 低内存回收机制(LMK)
当系统内存不足时,低内存杀手(Low Memory Killer,LMKD) 会根据 AMS 维护的 oom_adj
列表,按"从低优先级到高优先级"的顺序终止进程:
- AMS 通过
ProcessList
向内核注册内存阈值(如critical
、low
等),当内存低于阈值时触发回收。 - 回收策略:优先杀空进程 → 后台进程 → 服务进程(非重要),尽量保留可见和前台进程。
- 进程被杀死后,其内部组件状态会被 AMS 保存(如 Activity 的
onSaveInstanceState
),用户下次访问时重新创建进程并恢复状态。
6. 特殊进程类型
- Persistent 进程 :系统核心进程(如
phone
进程、systemui
进程)在AndroidManifest.xml
中声明persistent="true"
,oom_adj
极低(接近前台进程),除非系统崩溃否则不会被杀死,重启后会自动恢复。 - 缓存进程:即后台进程和空进程的统称,AMS 会维护一个 LRU(最近最少使用)列表,回收时优先杀死最久未使用的缓存进程。
总结
Android 进程模型的核心是"优先级驱动的动态生命周期管理 ":通过 AMS 实时调整进程优先级,结合 LMKD 实现内存的高效利用,同时保证用户可见和交互的进程优先存活。这种设计既满足了多应用并发的需求,又在有限的移动设备内存中平衡了性能与用户体验。对于系统开发,理解 oom_adj
计算逻辑、进程与组件的绑定关系,以及 LMK 的触发机制,是优化系统流畅度和内存占用的关键。
为什么 AMS 通过 Zygote 进程 fork 新进程可以避免重复初始化虚拟机,提升启动速度?
AMS 通过 Zygote 进程 fork 新应用进程能避免重复初始化虚拟机并提升启动速度,核心原因在于 Zygote 进程的"预初始化"和 Linux fork 机制的"写时复制(Copy-On-Write)"特性,二者结合实现了资源复用和启动加速。具体可从以下几个层面理解:
1. Zygote 进程的"预加载"机制
Zygote 是 Android 系统启动时最早创建的用户进程之一(由 init 进程启动),其核心职责是提前初始化应用运行所需的共性资源,避免每个应用进程重复执行这些耗时操作:
- 虚拟机(ART/Dalvik)初始化:Zygote 进程启动时会先初始化 ART 虚拟机(加载虚拟机核心库、设置运行时环境、初始化垃圾回收机制等),这一过程涉及大量底层库加载和内存分配,耗时较长。
- 框架资源预加载 :Zygote 会提前加载 Android 框架层的核心类(如
Activity
、Service
、Intent
等,通过preloadClasses
列表)、系统资源(如resources.arsc
、主题、drawable 等,通过preloadResources
)以及共享库(如libandroid_runtime.so
)。 - 系统服务连接 :Zygote 会提前与
system_server
进程建立通信(如注册到 AMS),为后续 fork 子进程后的组件启动铺路。
这些预加载操作在 Zygote 进程启动时一次性完成,之后所有应用进程通过 fork Zygote 诞生,天然继承这些已初始化的资源,无需重复执行。
2. Linux fork 的"写时复制(Copy-On-Write)"特性
Linux 的 fork 系统调用创建子进程时,并非立即复制父进程的全部内存(否则会导致内存浪费和耗时),而是采用 "写时复制(COW)" 策略:
- 初始阶段:子进程与父进程共享同一块物理内存(包括 Zygote 预加载的虚拟机、框架类、资源等),仅复制页表(虚拟内存到物理内存的映射关系),这一步几乎瞬时完成。
- 修改时复制:当子进程需要修改某块内存(如应用自己的代码、数据)时,系统才会为子进程复制该内存页的物理副本,父进程的内存保持不变。
对应用进程而言,Zygote 预加载的大部分资源(如框架类、虚拟机代码)是只读的(应用不会修改),因此子进程可以长期共享这些内存,无需复制。这既节省了内存,又避免了重复加载的时间开销。
3. 对比"独立启动"的劣势
若不通过 Zygote fork,而是让每个应用进程独立启动,会存在两大问题:
- 重复初始化成本:每个应用都要单独启动虚拟机、加载框架类和资源,这些操作占应用启动时间的 30%~50%(尤其是虚拟机初始化),导致启动缓慢。
- 内存浪费:每个进程都独立持有一份框架资源副本,而这些资源在所有应用中是通用的,会导致内存占用激增(例如,仅框架类就可能占用数十 MB 内存,多应用叠加后浪费严重)。
4. Zygote 与 AMS 的协同流程
当 AMS 需要启动新应用进程时(如首次启动 Activity 且目标进程不存在),流程如下:
- AMS 通过
Process.start()
向 Zygote 发送请求(携带应用包名、UID、进程名等参数)。 - Zygote 接收到请求后,调用
fork()
创建子进程(应用进程),子进程继承 Zygote 的虚拟机、预加载资源。 - 子进程启动后,通过
ActivityThread.main()
初始化自身(绑定 AMS、创建应用上下文等),但无需重复初始化虚拟机和框架资源。 - 子进程通过 IPC 向 AMS 报告"已就绪",AMS 随后调度组件(如 Activity)启动。
整个过程中,fork 操作因 COW 特性几乎无延迟,子进程直接复用 Zygote 的预加载资源,大幅缩短了启动时间。
总结
Zygote 本质是一个"资源预加载容器 ",而 Linux fork 的 COW 机制则是"高效复用资源的载体"。AMS 通过 Zygote fork 新进程,本质上是借助二者的协同,将应用启动的"共性初始化成本"从"每个进程重复执行"优化为"Zygote 一次性执行,所有子进程共享",从而从根源上避免了重复劳动,实现了启动速度的提升。这一设计是 Android 针对移动设备"内存有限、启动速度敏感"特点的关键优化。