想象 Android 系统是一个庞大的工厂(手机),里面需要运行很多“工人”(App)。但工厂不能随便招人(创建进程),也不能养闲人(浪费内存),所以有一套严格的“工人”管理制度。
1. 工厂的启动与“祖师爷”:Zygote (受精卵)
-
故事: 工厂刚通电启动(开机),第一个员工叫
init(初始化专员)。他的重要任务之一就是孵化出一个特殊的“祖师爷”工人——Zygote。Zygote 可不是普通工人,他天生就带着整个工厂的基础技能包(核心类库如android.os.*,android.view.*,基本工具如Drawable图片模板、颜色模板等)。他启动的方式也很特别:/system/app_process --start-system-server这条指令就像给他的启动手册。 -
技术核心:
Zygote由init进程启动。- 它预加载了大量 Java 核心类(
Preload Classes, 参考frameworks/base/preloaded-classes)和系统资源(Preload Drawables/Color State List, 参考res/values/arrays.xml)。 - 它启动后,会建立一个“招聘信箱”(Unix Domain Socket, 名字就叫
zygote),专门用来接收来自“大管家”(后面会讲到)的招工请求。 - Zygote 启动完,它的“大脑”(地址空间)里就装满了工厂的基础设施蓝图(预加载的类库资源)。
2. “大管家”诞生:System Server (系统服务)
-
故事: Zygote 祖师爷孵化出来后,做的第一件大事就是按照“启动手册”的指示(
--start-system-server),生下了工厂的“大管家”—— System Server 进程。这个大管家是整个工厂运作的核心,权力很大(拥有很高的权限CAP_KILL等,参考capability.h)。大管家一出生(startSystemServer),就由 Zygote 帮忙配置好了身份(用户组 IDAID_SYSTEM,参考android_filesystem_config.h)。 -
技术核心:
System Server是Zygotefork 出来的第一个子进程。- 它继承了 Zygote 预加载的类库。
- 它启动后,首先初始化底层硬件驱动(
Init1()- Native 层服务),然后启动所有重要的 Java 层管理服务(Init2()-ServerThread.run()),比如负责管理工人的“人事部”(ActivityManagerService- AMS)、负责管理工人窗口位置的“调度部”(WindowManagerService- WMS)等等。这些服务共同构成了 Android 框架的核心。 - 大管家启动后,它的“大脑”里也装满了基础蓝图,并且开始运行工厂的核心管理程序。
3. 招聘新“工人”:应用程序进程启动
-
故事: 当用户想打开一个 App(比如微信),就相当于工厂需要招聘一个新的“微信工人”。这个请求会先提交给大管家(
ActivityManagerService)。- 大管家(AMS)检查需求后,发出招聘指令:
startProcessLocked()。 - 指令层层下达:
Process.start()->Process.startViaZygote()。 - 最终,一个“招聘请求”(包含 App 包名、需要的权限等参数)被塞进了祖师爷 Zygote 的“招聘信箱”(Socket)。
- 大管家(AMS)检查需求后,发出招聘指令:
-
技术核心:
-
ActivityManagerService.startProcessLocked()是起点。 -
Process类通过zygoteSocket 发送参数(zygoteSendArgsAndGetResult)。 -
Zygote 从 Socket (
ZygoteConnection.runOnce()) 收到请求。 -
Zygote 使用
fork()魔法(快照复制自己),瞬间“克隆”出一个新的、几乎空白的“工人胚胎”进程(handleChildProc)。 -
新工人胚胎立刻开始“培训”(
RuntimeInit.zygoteInit()):- 建立自己的通信渠道(
nativeZygoteInit()- 启动 Binder 线程池)。 - 加载 App 自己的技能包(
applicationInit()- 调用ActivityThread.main()),成为真正的“微信工人”开始工作。
- 建立自己的通信渠道(
-
关键魔法: Zygote 的
fork()利用了 Linux 的 Copy-on-Write (COW) 机制。新进程(工人)刚诞生时,和 Zygote(祖师爷)共享大部分“基础技能”(预加载的类库和资源)。只有当新工人需要修改某些“技能”时,才会真正复制一份出来。这极大提高了“招工”效率,节省了内存!
-
4. 工厂内存紧张:“工人”退休制度 (进程回收)
-
故事: 工厂空间(内存)有限。当订单太多(运行 App 太多),空间快不够用时,就需要让一些不太重要的工人“退休”(杀死进程),腾出空间给更重要的工作。工厂有两套退休方案:
-
方案A:自动清洁机器人 - Low Memory Killer (LMK)
-
大管家(AMS)给每个工人贴了一个“重要等级”标签(
oom_adj值),范围是-16(最重要) 到15(最不重要)。 -
等级规则:
-16(SYSTEM_ADJ):大管家自己(System Server),绝不能退休。-12(PERSISTENT_PROC_ADJ):常驻工头(如电话 App),非常重要。0(FOREGROUND_APP_ADJ):正在和用户直接交流的工人(有前台 Activity)。1(VISIBLE_APP_ADJ):用户能看到的工人(有可见 Activity)。2(PERCEPTIBLE_APP_ADJ):用户能感知到的工人(比如在放音乐的服务)。5(SERVICE_ADJ):普通后台服务工人。6(HOME_APP_ADJ):桌面工人。7(PREVIOUS_APP_ADJ):用户上一个使用的工人。9-15(HIDDEN_APP_MIN_ADJ - MAX_ADJ):完全躲在后台的工人,很久没干活的等级就高(越不重要)。
-
特殊规则: 如果一个工人(A)正在为另一个更重要的工人(B,等级低)干活(比如提供数据或服务),那么工人A的等级会被提升(降低
oom_adj),变得和工人B一样重要(或至少不会低于前台App等级)。 -
清洁流程: LMK 机器人监控着工厂的剩余空间(内存)。当空间降到某个阈值(比如只剩很少了),它就根据预先设定的规则表(
oom_adj_to_min),查找当前哪个等级(oom_adj >= j)的工人可以被清理。它会优先干掉 等级最高(最不重要)且占空间最大 的工人。
-
-
方案B:大管家主动裁员 - AMS / WMS
-
大管家(AMS)和调度部(WMS)非常聪明,它们会在工人状态发生关键变化时主动检查是否该让某些工人退休:
- 工人停止了一项重要任务(
activityStopped)。 - 工厂规定了最多只能有多少工人(
setProcessLimit)。 - 工人注销了广播接收器(
unregisterReceiver/finishReceiver)。
- 工人停止了一项重要任务(
-
它们也负责清理那些“公司已经倒闭”(App 被卸载)但工人还在的幽灵进程。
-
-
-
技术核心:
-
oom_adj是核心: 由 AMS 根据进程中运行的组件(Activity/Service 等)状态动态计算。 - LMK 驱动: 内核模块,基于
oom_adj和内存阈值进行回收。 - 主动回收: AMS 在特定生命周期回调点调用
updateOomAdjLocked()更新进程oom_adj并可能触发回收 (killProcessesLocked)。
-
总结:Android 工厂的“工人”管理哲学
- 高效“造人”: 利用 Zygote 预加载 +
fork()+ Copy-on-Write,让创建新 App 进程变得极其快速且节省内存。 - 精细化管理: 通过
oom_adj值精确量化每个 App 进程的重要性(优先级)。 - 内存危机应对: 结合被动监控(Low Memory Killer 驱动)和主动管理(AMS/WMS),在内存不足时,严格按优先级从低到高(
oom_adj从大到小)回收进程,优先保障前台用户体验和系统核心服务的运行。 - 依赖关系处理: 如果一个进程服务于更重要的进程,它的重要性会相应提升(
oom_adj降低),避免误杀关键后台服务。
这篇文章就是详细讲解了 Android 系统如何像一个精密的工厂一样,高效地创建(孵化)应用程序进程,并在内存资源紧张时,智能地根据重要性(优先级)回收(退休)进程,从而保证整个系统流畅、稳定运行的核心机制。Zygote、System Server、AMS、WMS 和 Low Memory Killer 就是这套机制的五大关键角色。