Zygote fork准备
Zygote 的“fork 准备”包含两个时间点。ZygoteInit.main() 在预加载结束后调用一次 gcAndFinalize(),清理启动阶段产生的垃圾并填充 ART dex cache;之后每次创建 SystemServer、应用或 USAP 前,ZygoteHooks.preFork() 再停止 ART daemon、等待注销动作并保存 native token。两者解决的问题不同:前者改善共享堆的初始状态,后者保证 fork() 瞬间不会有不受控的后台线程继续运行。
1. 启动后的GC
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
相关函数:gcAndFinalize()
private static void gcAndFinalize() {
ZygoteHooks.gcAndFinalize();
}ZygoteInit 只是转发入口,真正实现位于 libcore 的 ZygoteHooks。SS006 的 preload() 完成后,SS004 的 main() 立即调用这里;它发生在创建 ZygoteServer 并 fork SystemServer 之前。
2. GC实现
源码文件:libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
public static void gcAndFinalize() {
final VMRuntime runtime = VMRuntime.getRuntime();
/* Zygote 没有 HeapWorker 线程,因此同步执行 finalizer。 */
System.gc();
runtime.runFinalizationSync();
cleanLocaleCaches();
System.gc();
}第一次 System.gc() 回收预加载和启动临时对象;runFinalizationSync() 在没有 HeapWorker 的 Zygote 中同步执行 finalizer;cleanLocaleCaches() 清理 BaseLocale/Locale 的 stale entries;第二次 GC 回收清理过程中产生的临时对象。这里的目标是 fork 前减少不必要的脏页和跨进程重复清理,不是保证堆完全为空。
3. Locale cache清理
源码文件:libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
private static void cleanLocaleCaches() {
BaseLocale.cleanCache();
Locale.cleanCache();
// 触发 ICU BaseLocale cache 的 stale entry 清理。
new ULocale.Builder()
.setLanguage("en")
.setRegion("US")
.build();
}Locale cache 清理是 Zygote 特有的 fork 优化:如果不在父进程清理,多个子进程可能分别处理相同 stale entry 并产生额外写时复制。最后构造 en-US 并非业务初始化,而是借助一次合法 locale 查找触发 ICU 内部 cache 的清理路径。
4. Fork前栅栏
源码文件:libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
private static long token;
public static void preFork() {
Daemons.stop();
// Daemons 已 join,但可能仍在执行 Unregister;nativePreFork 会等待它们。
token = nativePreFork();
}preFork() 不是只在 Zygote 初次启动时运行,而是每次 fork 前运行。Daemons.stop() 停止 ART daemon;注释明确说明 stop/join 后仍可能有 unregister 工作,nativePreFork() 负责等待这些 native 侧动作并返回后续 postForkChild() 所需的 token。这个 token 是 fork 状态的 owner 之一,不能跨 fork 调用复用或由 Java 业务代码自行生成。
5. fork后的父子恢复
源码文件:libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
public static void postForkChild(int runtimeFlags,
boolean isSystemServer, boolean isChildZygote,
String instructionSet) {
nativePostForkChild(token, runtimeFlags, isSystemServer,
isChildZygote, instructionSet);
if (!isChildZygote) {
inZygoteProcess = false;
}
Math.setRandomSeedInternal(System.currentTimeMillis());
}
public static void postForkCommon() {
nativePostZygoteFork();
Daemons.startPostZygoteFork();
}子进程先执行 postForkChild(),把 pre-fork token、runtime flags、SystemServer/child-zygote 身份和 instruction set 交给 native runtime;普通应用子进程退出 inZygoteProcess 状态并重置随机种子。随后父子进程都会执行 postForkCommon(),通知 ART fork 已完成并启动 post-zygote daemon。SystemServer 和 child zygote 有额外跳过项,例如 memory-mapped coverage,不应泛化为所有子进程相同。
6. 线程创建门槛
源码文件:libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java、frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote 初始化、预加载和 fork SystemServer
ZygoteHooks.stopZygoteNoThreadCreation();启动初期的 startZygoteNoThreadCreation() 防止预加载阶段随意创建线程;直到 native 状态和初始预加载完成后,stopZygoteNoThreadCreation() 才解除限制。每次 fork 前的 preFork() 则通过停止 daemon 达到另一种“fork 瞬间无后台工作”保证。前者是启动阶段的全局门槛,后者是每次 fork 的短暂栅栏。
7. 失败与时机
System.gc()/finalization:通常不以业务异常返回;它们影响 fork 前堆状态,耗时应通过 Zygote timing trace 观察。- Locale cache 清理:构造 locale 触发的内部清理若抛异常,会使
gcAndFinalize()失败并阻止后续 fork。 Daemons.stop()/nativePreFork():发生在每次 fork 之前,native 错误会中断 fork 主线,子进程尚未产生。nativePostForkChild():只影响子进程恢复;失败可能导致子进程无法进入 RuntimeInit/SystemServer。postForkCommon():父子都执行,负责重新允许 ART daemon 工作;不能只在子进程调用。
8. 导航与后续
# Zygote 启动后的 GC 调用点
rg -n "gcAndFinalize|PostZygoteInitGC" \
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
# GC、Locale cache、daemon 和 fork hooks
rg -n "gcAndFinalize|cleanLocaleCaches|preFork|postForkChild|postForkCommon|Daemons\.stop|nativePreFork" \
libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
# fork 调用方和父子分支
rg -n "ZygoteHooks\.preFork|nativeForkAndSpecialize|postForkChild|postForkCommon" \
frameworks/base/core/java/com/android/internal/os/Zygote.java \
frameworks/base/core/jni/com_android_internal_os_Zygote.cpp下一篇将分析 Zygote64/Zygote32 的双进程配置和 ABI 路由;不会重复本文的 GC、daemon 停止和 fork hooks。
