Every Android app starts as a clone of Zygote — a pre-warmed process that already has the Android framework loaded. Understanding this mechanism reveals why Android apps start in milliseconds instead of seconds.
What Is Zygote?
Zygote is a special process started by init during device boot. It preloads:
- The ART/Dalvik runtime
- Common framework classes (
Activity,View,Context, etc.) - Resources and drawables from the framework
- OpenGL/Skia libraries
All of this preloading happens once at boot time. When your app starts, it gets a copy of this work for free.
How App Launch Works
init process
└─ Zygote (preloaded at boot)
│
├─ fork() ──► com.example.myapp
├─ fork() ──► com.android.systemui
└─ fork() ──► com.google.android.gms
- User taps your app icon
- Launcher asks
ActivityManagerService(AMS) to start the Activity - AMS sends a request to Zygote over a Unix socket
- Zygote calls
fork()— creating a child process with all preloaded state - The child process runs
ActivityThread.main(), which callsApplication.onCreate()then starts the target Activity
Copy-on-Write: Free Memory Sharing
fork() uses Copy-on-Write (CoW). The child process initially shares all memory pages with Zygote (zero copy cost). Pages are only copied when either process modifies them.
This means:
- Framework classes loaded by Zygote are shared across ALL app processes
- A device with 30 apps doesn't have 30 copies of
Activityin RAM — just one - Your app's unique pages (your classes, your objects) are where memory actually diverges
# See Zygote's preloaded class count
adb shell cat /proc/$(adb shell pidof zygote)/maps | grep ".dex" | wc -l
WebView Zygote
Android maintains a separate WebView Zygote (com.android.webview) that preloads Chromium libraries. WebView rendering runs in an isolated process forked from this Zygote — providing security isolation between web content and your app.
Impact on Cold Start
Cold start time breaks down into:
- Fork Zygote (~1–5ms) — fast, OS-level operation
- bindApplication (~50–200ms) — JVM setup, your Application class
- ContentProvider init — often a hidden bottleneck
- Activity creation — inflate layout, bind data
# Measure cold start time
adb shell am force-stop com.example.myapp
adb shell am start-activity -W com.example.myapp/.MainActivity
# Output: TotalTime: 342ms
The Zygote fork itself is not your bottleneck. Your Application.onCreate() and ContentProvider initializations typically are.
Practical Implications
Keep Application.onCreate() fast:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// ❌ Don't do heavy init synchronously
// database.init() // could take 100ms+
// sdk.setup() // often 50-200ms
// ✅ Use App Startup library for lazy/ordered init
// Initializers run only when their component is first needed
}
}
Baseline Profiles accelerate the warm-up phase by pre-compiling your app's hot code paths during install, reducing JIT compilation time on first run.
Key Takeaways
| Concept | Detail |
|---|---|
| Zygote | Pre-warmed process; avoids re-loading framework on every launch |
fork() | OS primitive; creates child with all Zygote's state |
| Copy-on-Write | Framework memory shared until modified — efficient RAM usage |
| App start | AMS → Zygote socket → fork → ActivityThread.main() |
| Your bottleneck | Application.onCreate(), ContentProviders, not the fork itself |