如果说 init 进程是 Android 的"管家",那么 Zygote 就是真正的"造物主"。
当你点击应用图标时,系统并没有从头开始加载 Java 虚拟机、读取几千个框架类文件。相反,它直接"复制"了一个已经准备好一切的 Zygote 副本。这就是 Zygote(受精卵) 名字的由来------它是所有 Android App 进程的母体。
本篇我们将深入 frameworks/base/core/jni 和 frameworks/base/java/com/android/internal/os,揭开 Zygote 预加载 ART 虚拟机、共享内存以及"秒开"App 的核心秘密。
第一步:从 C++ 到 Java ------ app_main.cpp 的接力
Zygote 的本质是一个可执行文件 /system/bin/app_process。它的入口在 C++ 层。
代码位置 :frameworks/base/cmds/app_process/app_main.cpp
arduino
// 文件:frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]) {
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// 1. 解析参数 (如 --zygote, --start-system-server)
// 2. 启动 ART 虚拟机
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
return 0;
}
runtime.start 内部做了两件大事:
- 创建 JavaVM :调用 JNI 接口
JNI_CreateJavaVM,启动 ART 运行时。 - 反射调用入口 :找到 Java 类
ZygoteInit的main方法并执行。
关键点:此时,C++ 的世界退居幕后,Java 的世界正式拉开帷幕。
第二步:ZygoteInit 的"备战" ------ 预加载机制
进入 Java 层后,ZygoteInit.main 开始执行。这是 Zygote 最核心的"备战"阶段。
代码位置 :frameworks/base/java/com/android/internal/os/ZygoteInit.java
scss
// 文件:frameworks/base/java/com/android/internal/os/ZygoteInit.java
public static void main(String[] argv) {
// 1. 注册 Zygote 信号处理 (处理 SIGUSR1 等)
ZygoteHooks.startZygoteNoChildCreation();
// 2. 【核心】创建 ServerSocket,监听 Socket 连接
// 端口通常是抽象命名空间 "android:zygote"
runSelectLoop(abiList);
// 3. 【核心】预加载类和资源 (只有 Zygote 做,子进程共享)
preload();
// 4. 启动 SystemServer (系统的核心服务进程)
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
// 5. 进入无限循环,等待 fork 请求
runSelectLoop(abiList);
}
预加载做了什么? (preload())
scss
private static void preload() {
// 1. 预加载常用 Java 类 (约 200+ 个)
// 如: HashMap, ArrayList, ActivityThread, ContextImpl
preloadClasses();
// 2. 预加载系统资源 (framework-res.apk)
// 将图片、字符串、布局解析到内存,所有 App 共享
preloadResources();
// 3. 预加载图形库 (OpenGL, EGL)
// 初始化 GPU 驱动上下文
preloadGraphics();
// 4. 预加载文本渲染器 (HarfBuzz)
preloadTextResources();
}
效果:这些操作只需做一次。当 App 启动时,这些类和资源已经存在于内存中,且标记为"只读",子进程直接共享,无需重新加载。
第三步:Fork 的魔法 ------ 写时复制 (Copy-On-Write)
Zygote 如何瞬间生成一个新进程?答案是 Linux 的 fork() 系统调用,配合 写时复制 (COW) 机制。
原理图解:
-
Fork 前:Zygote 拥有巨大的内存空间(包含 ART Heap, 预加载类, 资源)。
-
Fork 瞬间 :内核复制 Zygote 的页表,但不复制物理内存 。父子进程指向同一块物理内存,标记为 Read-Only。
-
运行后:
- 如果子进程只读访问:直接使用共享内存(零开销)。
- 如果子进程写入数据(如创建新对象):内核触发缺页中断,单独分配一块新物理内存给子进程,复制修改页的内容。
伪代码:Zygote 处理 Fork 请求
scss
// 文件:frameworks/base/java/com/android/internal/os/ZygoteConnection.java
void processOneCommand(ZygoteConnection newConn) {
// 1. 从 Socket 读取参数 (UID, GID, 类名, 参数等)
Arguments args = readArguments(newConn);
// 2. 【关键】调用 nativeForkAndSpecialize
pid = Zygote.nativeForkAndSpecialize(
args.uid, args.gid, args.gids,
args.debugFlags, rlimits, args.mountExternal,
args.seInfo, args.niceName, fdsToClose,
fdsToIgnore, args.instructionSet, args.appDataDir
);
if (pid == 0) {
// --- 子进程视角 (App 进程) ---
// 关闭 ServerSocket,只保留客户端 Socket
closeServerSocket();
// 跳转到 App 的入口 (ActivityThread.main)
handleChildProc(args);
} else {
// --- 父进程视角 (Zygote) ---
// 记录新进程 PID,继续监听下一个请求
recordPid(pid);
}
}
底层 C++ 实现:
scss
// 文件:frameworks/base/core/jni/AndroidRuntime.cpp
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(...) {
pid_t pid = fork(); // 系统调用
if (pid == 0) {
// 子进程:清理不必要的 FD,设置 UID/GID (降权)
specializeAppProcess(...);
}
return pid;
}
硬件视角:
- TLB (页表缓存) :Fork 后会刷新 TLB,因为页表变了。
- 内存带宽:由于 COW 机制,启动初期几乎不消耗内存带宽,极大提升了启动速度。
🔹 第四步:SystemServer 的诞生 ------ 特殊的"长子"
Zygote 孵化的第一个、也是最特殊的子进程是 SystemServer。它不同于普通 App,它承载了 AMS, WMS, PMS 等核心服务。
分支流程图 : 
通信协议 :
Zygote 与请求者(通常是 AMS)通过 LocalSocket 通信。
- 请求包:包含 UID, GID, 目标类名, 参数数组。
- 响应包:返回新进程的 PID,或者错误码。
第五步:Zygote 的生命周期 ------ 永动的孵化器
Zygote 启动后,永远不会退出(除非系统重启)。它在一个死循环中等待命令。
代码位置 :frameworks/base/java/com/android/internal/os/ZygoteInit.java
scss
// 简化的主循环
while (true) {
// 1. 等待 Socket 连接 (AMS 请求启动 App)
Runnable command = runWaitForConnection();
// 2. 处理命令 (Fork 新进程)
if (command != null) {
try {
command.run(); // 执行 fork 逻辑
} catch (Throwable t) {
Log.e("Zygote", "Error handling command", t);
}
}
// 3. 垃圾回收 (可选)
// 如果内存压力过大,Zygote 也会触发 GC,清理不再共享的对象
gcIfNecessary();
}
注意:Zygote 自身也会进行 GC。如果预加载的某些对象在子进程中都被修改了(导致 COW 分裂),Zygote 需要清理这些"脏"引用,保持自身的纯净,以便后续 Fork 能最大化共享内存。
🔹 本篇小结
| 阶段 | 关键动作 | 核心技术 | 收益 |
|---|---|---|---|
| 启动 | app_main.cpp -> ZygoteInit |
ART 虚拟机创建 | 建立 Java 运行环境 |
| 预加载 | preload() |
类加载 & 资源解析 | 减少 App 启动时间 50%+ |
| 监听 | ServerSocket |
LocalSocket 通信 | 接收 AMS 启动指令 |
| 孵化 | fork() |
Copy-On-Write (COW) | 节省内存 30%+ , 秒级启动 |
| 特例 | startSystemServer |
特殊 Fork 流程 | 启动系统核心服务 |
硬件与性能洞察:
- 内存利用率:通过 COW,几十个 App 进程共享同一份 Framework 代码,物理内存占用极低。
- CPU 调度:Fork 操作极快(微秒级),主要耗时在后续的类验证和资源加载(已被 Zygote 提前完成)。
下篇预告
Zygote 成功孵化了 SystemServer 。这个进程内部究竟藏着什么?
为什么它能管理所有的 App?
下篇我们将深入 SystemServer 的内部,拆解 Android 框架层的"三驾马车"。
敬请期待:《SystemServer:Android 框架层的心脏与大脑》