Android from the foundation · Puzzle 3 of 6

Zygote is why apps start fast

One process, started at boot, preloads ART and the framework. Every app is forked from it and shares its memory until it writes.

The problem

In puzzle 2, ART moved into every house. But starting ART and loading the thousands of framework classes takes seconds. If every app launch did that from scratch, every tap on an icon would be slow, and every app would hold its own copy of the same framework in memory.

See it

Zygote starts at boot with the framework loaded; forking makes a new app process that shares Zygote's memory pages; a page is copied only when the app writes to it Zygote ART + framework your appshares pages another appshares them too copied
  1. At boot, Android starts one process, Zygote, and loads ART and the framework into its memory once. Then it waits.

    Step 1 of 4
  2. You tap an icon. Zygote forks: the kernel makes a new process that starts as a copy of Zygote, switches it to your app's UID, and loads your code.

    Step 2 of 4
  3. The new process doesn't get its own copy of the framework: it shares Zygote's memory pages. So does every other app. Predict: what if your app changes a shared page?

    Step 3 of 4
  4. Only then does the kernel copy that one page for your app: copy-on-write. Everything you don't change stays shared.

    Step 4 of 4

The building block

The kernel hands out memory in small fixed-size blocks called pages. To fork is to ask the kernel for a new process that starts as a copy of an existing one. The copy is cheap because the kernel doesn't duplicate the pages: parent and child share them, and a page is only copied when one of them writes to it (copy-on-write).

Zygote ("the first cell") starts at boot, loads ART and the common framework code, then waits. It also forks system_server, the town hall. When you launch an app, system_server asks Zygote to fork; the child switches to the app's UID and loads the app's code.

In the source, ZygoteInit.main()Zygote's main: preload the framework, fork system_server, then wait for "fork an app" requests. 817 public static void main(String[] argv) { … 885 preload(bootTimingsTraceLog); … 905 Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); … 919 caller = zygoteServer.runSelectLoop(abiList);ZygoteInit.java, line 817 · AOSP frameworks/base ↗ does exactly this: preload, start system_server, then wait for "fork an app" requests.

In practice: cold, warm, hot start

Your app has one Application object per process; its Application.onCreate() runs before any screen exists. Then come the screens (Activities).

  • Cold start: no process yet. Fork from Zygote, create the Application, then the first Activity.
  • Warm start: the process is alive, but the Activity has to be created again.
  • Hot start: process and Activity are alive; the app just comes back to the front. (Nothing to do with "hot code" in puzzle 2.)

The analogy

Zygote is a show house, fully furnished at boot. Each new app house is a copy of it, but the furniture isn't duplicated: every house uses the show house's furniture until it repaints a chair. Only then does it get its own copy of that chair.

Check yourself

Why is heavy work in Application.onCreate() bad?

Not quite. The framework pages are shared, but your code runs only in your process. It's your users who wait.

Not quite. Zygote's fork only copies Zygote. Your Application is created in your own process, after the fork.

Right. On a cold start your Application is created before the first screen, so users stare at a blank screen while it runs.

Ten apps are running. How many copies of the preloaded framework are in memory?

Not quite. Fork starts the child as a copy, but the kernel shares the pages instead of duplicating them.

Right. Forked apps share Zygote's pages until they write to them (copy-on-write).

Not quite. Recency doesn't matter: every forked app shares Zygote's pages.

Your app changes one shared page. What happens?

Right. Copy-on-write copies only the page that's written, and only for the process that wrote it.

Not quite. Only the page you write to is copied; everything else stays shared.

Not quite. That's exactly what copy-on-write prevents: your change goes into your own copy.

Sources

What you now know

Cold start = fork Zygote, create your Application, create your first Activity. Anything slow in Application.onCreate() is paid on every cold start, before the user sees anything.