一、 Zygote 的设计初衷与核心价值
在理解Zygote如何工作之前,首先要明白它为什么存在。
1. 要解决的问题: 想象一下,如果没有Zygote,系统每次启动一个新的App进程都需要:
- 单独启动一个JVM
- 重新加载一遍Android Framework的所有类(如
Activity,View,Context等,数千个类) - 重新加载系统资源(如系统主题、字符串等)
这会导致:应用启动极慢 、内存消耗巨大(每个进程都有一份相同的类和资源的副本)。
2. Zygote的解决方案: Zygote采用了经典的 "预加载 + 进程孵化 (fork)" 模式,其核心思想是 "空间换时间" 和 "共享内存"。
- 预加载:在系统启动初期,由Zygote进程一次性完成所有App公共需要的类和资源的加载工作。
- 进程孵化 :当需要启动新App时,Zygote通过
fork()系统调用分裂自身,创建出一个子进程。这个子进程天然继承了父进程(Zygote)已经加载的所有内存状态。
二、 Zygote 的启动与初始化过程
Zygote的启动是一条明确的链条:
Init进程 -> 解析init.rc -> 启动app_process -> 执行ZygoteInit.java
详细步骤:
-
由Init进程启动 : 在
init.zygote32.rc或类似的配置文件中,有类似以下的定义:bashservice zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state onservice zygote:定义了一个名为"zygote"的服务。/system/bin/app_process:这是Zygote的真正可执行文件,它不是一个普通的JAR,而是一个可以启动Android运行时环境的原生程序。--zygote --start-system-server:参数,告诉app_process以Zygote模式启动,并孵化System Server。
-
创建虚拟机并预加载:
app_process执行后,会创建第一个Android Runtime (ART) 实例。- 随后进入Java世界,执行
ZygoteInit.java的main()方法。 - 在
main()方法中,Zygote会进行一项繁重但关键的工作:预加载 。- 预加载类 :调用
preloadClasses()。它会读取/system/etc/preloaded-classes文件(一个包含数千个常用类的列表),然后使用Class.forName()逐个加载。这包括了所有的Android Framework类。 - 预加载资源 :调用
preloadResources()。这会加载系统的通用资源,如framework-res.apk中的drawables、xml配置等。 - 预加载共享库 :加载一些必要的JNI库,如
libandroid.so等。
- 预加载类 :调用
-
启动System Server:
- 预加载完成后,Zygote会
fork()出第一个也是最重要的子进程------System Server。 - System Server承载了Android中所有关键的系统服务(如AMS, WMS, PMS等)。
- 预加载完成后,Zygote会
-
进入循环,监听Socket:
- 孵化完System Server后,Zygote父进程会关闭不必要的Socket副本。
- 然后调用
runSelectLoop()方法,进入一个无限循环,监听在/dev/socket/zygote上的Socket连接,等待ActivityManagerService发来的启动新应用的请求。
三、 Zygote 孵化新应用进程的详细原理
这是Zygote最精妙的部分。当用户点击App图标时:
-
AMS发送请求:
ActivityManagerService决定要启动一个应用,但它自己没有权限创建新进程。- AMS通过
Process.start()方法,向Zygote的Socket发送一个启动新进程的请求消息。这个消息包含了新进程的必要参数:如uid,gid,主类名(通常是android.app.ActivityThread),参数等。
-
Zygote处理请求:
- 在
runSelectLoop()中监听到Socket连接的Zygote进程,会fork()自身,创建一个子进程。 - 关键点 :这个子进程是Zygote的完整副本,它继承了:
- 已经初始化的ART虚拟机。
- 所有预加载的Java类。
- 所有预加载的资源和共享库。
- 系统的
LD_LIBRARY_PATH等环境。
- 在
-
子进程的"特化":
- 子进程虽然继承了一切,但它需要成为一个独立的App进程,而不是另一个Zygote。
- 在
fork()之后,子进程会:- 关闭从Zygote继承来的、不需要的Socket连接。
- 设置进程名 (从"zygote"变为包名,如
com.example.app)。 - 设置UID/GID,进行应用沙盒隔离。
- 最后,调用
ZygoteInit.zygoteInit()->RuntimeInit.applicationInit()-> 找到AMS传过来的mainClass(即ActivityThread),并执行其main()方法。
ActivityThread.main()被调用,这意味着App进程的主线程(UI线程)正式开始运行,它会创建Application对象,然后启动主Activity。
四、 Zygote 的核心机制:写时复制
这是Zygote能够节省内存的基石。
- 原理 :当Zygote通过
fork()创建子进程时,内核并不会立即复制父进程的内存空间给子进程。相反,它让父进程和子进程共享同一份物理内存页 ,并将这些内存页标记为只读。 - 写入时复制 :当父进程或子进程试图修改 某一内存页时,内核会捕获这个"写操作"异常,然后仅为试图修改的进程复制一份该内存页的副本,让其在这个副本上进行修改。而其他仍然只读的内存页则继续保持共享。
带来的巨大优势:
- 内存效率 :100个App可能共享了90%的Framework代码和资源。如果没有COW,内存占用将是
100 * Zygote内存。有了COW,内存占用接近于Zygote内存 + 100 * (每个App私有内存)。 - 启动速度 :因为不需要重新加载类和资源,
fork()一个进程并"特化"它,比从头启动一个JVM并加载所有东西要快得多。
五、 Zygote 的变种与演进
-
WebView Zygote:
- 为了让WebView也享受预加载的好处,并实现与App主进程的隔离,Android引入了独立的
webview_zygote。它预加载了WebView相关的代码,专门用于孵化渲染Web内容的进程。
- 为了让WebView也享受预加载的好处,并实现与App主进程的隔离,Android引入了独立的
-
应用Zygote:
- 从Android 10开始,引入了"应用Zygote"。它允许某个应用在系统启动时提前初始化一个自己的Zygote进程,这样当需要启动该应用的某个组件时,可以快速从它的应用Zygote中fork,进一步加快启动速度。
"Zygote的设计是Android系统架构中的一个经典权衡。它通过一个复杂的启动时预加载过程,换取了所有应用运行时极致的启动速度和内存效率。这种设计完美地契合了移动设备资源有限、但要求响应迅速的特点。
理解Zygote,对于我们做应用启动优化也有指导意义。比如,我们知道Zygote已经预加载了Framework类,所以应用自己的Application和首个Activity的初始化就应该尽量轻量,避免在主线程做繁重操作,这样才能最大化利用Zygote带来的启动优势。"