Skip to content

Zygote预加载

按真实源码拆解 Zygote preload 的 classes、resources、libraries、文本资源、兼容规则与 lazy 条件。

基于android-17.0.0_r1
AndroidZygoteART源码阅读

Zygote预加载 ​

Zygote 的预加载不是“启动时把所有 Framework 都读一遍”。Android 17 的 ZygoteInit.preload() 把类、非 boot classloader、系统资源、App Process HAL、图形驱动、共享库、字体/连字符、兼容性规则和 WebView 初始化排成一条 fork 前流水线。每一项都有不同的 owner 和失败语义:类预加载会处理缺失类和静态初始化异常,资源预加载捕获运行时错误,图形驱动由属性控制,而 sPreloadComplete 只表示整条方法成功走到末尾。

1. 总入口 ​

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

相关函数:preload()、lazyPreload()

java
static void preload(TimingsTraceLog bootTimingsTraceLog) {
    Log.d(TAG, "begin preload");
    bootTimingsTraceLog.traceBegin("BeginPreload");
    beginPreload();
    bootTimingsTraceLog.traceEnd();
    bootTimingsTraceLog.traceBegin("PreloadClasses");
    preloadClasses();
    bootTimingsTraceLog.traceEnd();
    bootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders");
    cacheNonBootClasspathClassLoaders();
    bootTimingsTraceLog.traceEnd();
    bootTimingsTraceLog.traceBegin("PreloadResources");
    Resources.preloadResources();
    bootTimingsTraceLog.traceEnd();
    nativePreloadAppProcessHALs();
    maybePreloadGraphicsDriver();
    preloadSharedLibraries();
    preloadTextResources();
    preloadCompatConfig();
    WebViewFactory.prepareWebViewInZygote();
    endPreload();
    warmUpJcaProviders();
    sPreloadComplete = true;
}

static void lazyPreload() {
    Preconditions.checkState(!sPreloadComplete);
    preload(new TimingsTraceLog("ZygoteInitTiming_lazy", Trace.TRACE_TAG_DALVIK));
}

TimingsTraceLog 让每个阶段能在启动 trace 中单独计时;它不改变阶段顺序。lazyPreload() 复用同一 preload(),只允许在 sPreloadComplete == false 时调用,避免重复完整预加载。SS004 的 main() 决定是否第一次立即调用;后续代码可能在第一次 fork 前触发 lazy 路径。

2. 类预加载 ​

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

相关函数:preloadClasses()

java
is = new FileInputStream(PRELOADED_CLASSES);
BufferedReader br = new BufferedReader(
        new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);
while ((line = br.readLine()) != null) {
    line = line.trim();
    if (line.startsWith("#") || line.equals("")) continue;
    Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
    try {
        Class.forName(line, true, null);
        count++;
    } catch (ClassNotFoundException e) {
        if (!line.contains("$$Lambda$")) {
            Log.w(TAG, "Class not found for preloading: " + line);
        }
    } catch (UnsatisfiedLinkError e) {
        Log.w(TAG, "Problem preloading " + line + ": " + e);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
    }
}

/system/etc/preloaded-classes 是输入,boot classpath loader(null)是加载 owner,Class.forName(..., true, null) 会强制执行静态初始化。缺失 lambda 和普通缺失类区别处理;UnsatisfiedLinkError 记录 warning;其他 Error/RuntimeException 会重新抛出,阻止 Zygote 继续伪装成预加载完成。

文件读取结束后,finally 调用 VMRuntime.preloadDexCaches(),并在 profile boot classpath 时重置 JIT counters;如果此前从 root 降权,最后恢复 root。也就是说,“预加载了多少类”和“dex cache 已填充”是两个不同阶段。

3. 资源预加载 ​

源码文件:frameworks/base/core/java/android/content/res/Resources.java

相关函数:Resources.preloadResources()

java
public static void preloadResources() {
    try {
        final Resources sysRes = Resources.getSystem();
        sysRes.startPreloading();
        if (PRELOAD_RESOURCES) {
            TypedArray ar = sysRes.obtainTypedArray(
                    com.android.internal.R.array.preloaded_drawables);
            preloadDrawables(sysRes, ar);
            ar.recycle();
            ar = sysRes.obtainTypedArray(
                    com.android.internal.R.array.preloaded_color_state_lists);
            preloadColorStateLists(sysRes, ar);
            ar.recycle();
        }
        sysRes.finishPreloading();
    } catch (RuntimeException e) {
        Log.w(TAG, "Failure preloading resources", e);
    }
}

资源 owner 是 Resources.getSystem() 的预加载状态和 TypedArray 中列出的资源 ID。drawable/color state list 的加载失败会记录 warning,而不是像类静态初始化异常那样统一抛出;因此资源阶段完成不等于每一个资源都成功进入缓存。TypedArray.recycle() 是临时数组的清理责任,不能省略。

4. 库、图形与文本 ​

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

java
private static void preloadSharedLibraries() {
    System.loadLibrary("android");
    System.loadLibrary("jnigraphics");
    if (android.os.Flags.perfettoSdkTracingV3()) {
        System.loadLibrary("perfetto_framework_jni");
    }
    if (!SystemProperties.getBoolean(
            "config.disable_renderscript", false)) {
        System.loadLibrary("compiler_rt");
    }
}

private static void preloadTextResources() {
    Hyphenator.init();
    TextView.preloadFontCache();
}

private static void preloadCompatConfig() {
    CompatibilityRules.loadSystemRules();
}

共享库由 linker/ART 负责加载并进入 Zygote 地址空间;Perfetto 和 RenderScript 分支受 feature flag/property 控制。文本资源阶段由 Hyphenator 与 TextView 各自拥有缓存,兼容规则由 CompatibilityRules 读取系统规则。它们的目的都是让 fork 后子进程共享已初始化状态,但失败和可选性不同,不能统称为“预加载 Framework 类”。

5. fork前的生效时机 ​

预加载的共享收益只有在 fork 前完成初始化才成立;sPreloadComplete 在 warmUpJcaProviders() 之后才设为 true。Zygote 随后执行 gcAndFinalize(),再进入 forkSystemServer() 或命令循环。若使用 lazy preload,首次 fork 之前仍必须走完 preload(),否则子进程得到的只是未完成的共享基线。

6. 失败定位与导航 ​

bash
# 入口阶段和可选分支
rg -n "static void preload|lazyPreload|preloadSharedLibraries|preloadTextResources|preloadCompatConfig" \
  frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

# 类列表、静态初始化、dex cache 与降权恢复
rg -n "PRELOADED_CLASSES|Class.forName|preloadDexCaches|setreuid|setregid" \
  frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

# 资源清单、TypedArray 回收和 RuntimeException 边界
rg -n "preloadResources|preloaded_drawables|preloaded_color_state_lists|finishPreloading" \
  frameworks/base/core/java/android/content/res/Resources.java

看到 Zygote 启动慢时,先按 TimingsTraceLog 阶段定位,再区分类加载、资源读取、库初始化和图形驱动;看到预加载 warning 时,不要把资源 warning 与类静态初始化 fatal 视为同一失败等级。

下一篇将深入 /system/etc/preloaded-classes 的逐类加载和静态初始化,解释为什么 Zygote 要在该阶段暂时降低 root 权限。