Overview
This lesson provides a deep dive into the Zygote, the progenitor of all Android application processes. The Zygote architecture is one of Android's most critical design decisions, enabling extremely fast application startup times and massive memory savings across the OS.
The Zygote Concept
In biology, a zygote is the initial cell formed when two gamete cells are joined, which then divides to create a new organism. In Android, the zygote process boots up during system initialization, initializes the Android Runtime (ART), and waits. Every time the system needs to launch a new app, it doesn't start a new process from scratch. Instead, it asks the Zygote to "clone" itself.
Preloaded Classes and Resources
Starting an Android process from scratch requires initializing ART, parsing framework oat files, and loading thousands of core Java classes (like java.lang.String, android.view.View, etc.). This takes considerable time.
When the system boots, the Zygote process performs this heavy lifting once.
- Preloaded Classes: It reads a text file (
/system/etc/preloaded-classes) containing a list of thousands of heavily used framework classes and forces ART to load and initialize them into memory. - Preloaded Resources: It loads common UI resources (drawables, color state lists, strings) from the framework APK into memory.
Once the preloading is complete, the Zygote's heap contains everything a typical Android app needs to start executing.
fork() Semantics and Copy-on-Write (CoW)
When ActivityManager requests a new application process, the Zygote executes the Linux fork() system call.
fork() creates a child process that is an exact duplicate of the parent (the Zygote). Crucially, Linux implements fork() using Copy-on-Write (CoW).
When the child process is created, the kernel does not physically copy the Zygote's memory. Instead, both the Zygote and the new app process point to the exact same physical memory pages.
- Sharing: As long as neither process modifies a memory page, they share it. This means the hundreds of megabytes of preloaded classes and resources are shared perfectly among all running apps, saving massive amounts of RAM.
- Copying: If the app modifies a shared variable, the CPU triggers a page fault. The kernel intercepts this, transparently copies that specific memory page for the app, and allows the modification to proceed. Only modified pages consume extra RAM.
// AOSP reference: frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
// Simplified flow of handling a spawn request
Runnable processOneCommand(ZygoteServer zygoteServer) {
// ... read arguments from ActivityManager ...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, ...);
if (pid == 0) {
// We are in the child process (the new app)
zygoteServer.closeServerSocket();
return handleChildProc(parsedArgs, ...);
} else {
// We are in the parent process (Zygote)
return handleParentProc(pid, ...);
}
}
Zygote64 vs Zygote32
Modern Android devices are 64-bit but often maintain 32-bit compatibility. Because a 64-bit process cannot fork() a 32-bit process, the system actually runs two Zygotes:
zygote64: The primary Zygote for modern 64-bit applications.zygote(orzygote32): A secondary Zygote specifically for legacy 32-bit applications.
You can observe these processes running using adb shell ps -A | grep zygote.
App-Zygote for Isolated Processes
Android 10 introduced the concept of the App Zygote.
Certain applications, like web browsers, need to spawn numerous tightly sandboxed "isolated processes" to render different web pages securely. Spawning these from the main system Zygote is inefficient because they don't share the application's specific preloaded logic.
An App Zygote is a specialized zygote created for a specific application. It inherits from the system Zygote but can preload application-specific classes and resources. When the app needs a new isolated process, it forks from its own App Zygote, maximizing CoW sharing for the app's proprietary code.