4、Zygote进程优化:Zygote启动参数调优、预加载资源裁剪、Class预加载优化
Zygote,这个词在Android系统里,说白了就是「孵化器」的意思。所有应用进程,都是它fork出来的。所以Zygote的启动速度,直接决定了整个系统的开机时间。我做过不少项目,每次优化开机速度,Zygote这块都是必啃的硬骨头。
今天咱们就聊聊Zygote优化的三个核心方向:启动参数调优、预加载资源裁剪、Class预加载优化。这三个点,你吃透了,开机速度至少能快个10%-20%。
4.1 Zygote启动参数调优
Zygote启动时,会带一堆参数。这些参数,很多厂商都是默认值,其实大有文章可做。
先看一个典型的Zygote启动命令:
// 默认启动参数示例
/system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
这里面有几个关键参数,我一个个说。
4.1.1 堆内存参数调整
Zygote进程的堆内存,默认是128MB到256MB。但说实话,这个值偏保守了。我遇到过一台低端设备,Zygote启动时频繁触发GC,导致开机时间硬生生多了3秒。
为什么会这样?因为Zygote要预加载大量资源,堆太小,GC就频繁。GC一多,CPU就忙不过来。
我个人习惯,会把最小堆调到192MB,最大堆调到384MB。像这样:
// 调优后的参数
setprop dalvik.vm.heapsize 384m
setprop dalvik.vm.heapstartsize 16m
setprop dalvik.vm.heapgrowthlimit 192m
注意,heapgrowthlimit是单个应用能用的最大堆,别设太大,否则低端机扛不住。
4.1.2 并行GC与后台GC
Zygote启动时,默认是串行GC。这效率太低了。我建议开启并行GC和后台GC:
// 开启并行GC
setprop dalvik.vm.parallelGCThreads 2
setprop dalvik.vm.backgroundGcEnabled true
嗯,这里要注意:并行GC线程数别设太多,2-4个就够了。设多了,线程切换开销反而更大。
4.2 预加载资源裁剪
Zygote启动时,会预加载一堆资源。这些资源,很多应用根本用不到。说白了,就是白费功夫。
我记得有一次,帮客户优化一款低端机。开机时间要控制在18秒以内,但实测总是19秒多。一查,Zygote预加载了300多张图片,其中200张是系统主题资源。但那个设备,根本就没装第三方主题。
所以,资源裁剪的核心思路就一个:只加载真正需要的资源。
4.2.1 裁剪预加载图片
预加载图片的列表,在frameworks/base/core/res/res/values/arrays.xml里。像这样:
<!-- 预加载图片列表 -->
<string-array name="preloaded_drawables">
<item>@drawable/ic_menu_gallery</item>
<item>@drawable/ic_menu_help</item>
<item>@drawable/ic_menu_manage</item>
<!-- 这里可能有几百项 -->
</string-array>
我的做法是:先统计设备上最常用的50个应用,看看它们用了哪些drawable。没用的,直接删掉。一般能砍掉30%-40%的预加载图片。
4.2.2 裁剪预加载布局
除了图片,Zygote还会预加载一些布局文件。比如screen_simple.xml、alert_dialog.xml这些。但有些布局,比如keyguard_screen_touch_emergency.xml,在低端机上根本用不到。
我曾经在项目里,把预加载布局从120个砍到了80个。开机时间直接降了1.2秒。效果很明显。
4.3 Class预加载优化
Class预加载,是Zygote优化的重头戏。Zygote启动时,会加载大量Java类。这些类,很多应用根本不会用到。
你想想看,Zygote加载了1000个类,但实际只用了600个。那400个类,就是白白浪费了CPU和内存。
4.3.1 分析预加载类列表
预加载类的列表,在frameworks/base/config/preloaded-classes文件里。打开一看,密密麻麻几千行。
我的做法是:先跑一遍开机流程,用dumpsys meminfo和traceview看看哪些类被真正加载了。然后对比预加载列表,找出那些「只预加载、不实际使用」的类。
// 查看实际加载的类
adb shell dumpsys meminfo --oom zygote | grep "Class"
嗯,这里要注意:有些类虽然开机时没用,但后续系统服务会用到。别一刀切,要逐个分析。
4.3.2 延迟加载策略
对于确实需要、但开机时用不到的类,我建议用延迟加载。说白了,就是用到的时候再加载。
比如,android.media.MediaPlayer这个类,开机时基本用不到。但Zygote默认会预加载它。我的做法是:从预加载列表里删掉它,然后在SystemServer里按需加载。
4.3.3 使用ClassLoader优化
还有一个容易被忽略的点:ClassLoader的加载顺序。Zygote默认用的是PathClassLoader,它会从多个路径加载类。如果路径顺序不对,加载效率会很低。
我建议调整ClassLoader的搜索路径,把最常用的路径放在前面。像这样:
// 优化ClassLoader路径顺序
PathClassLoader classLoader = new PathClassLoader(
"/system/framework/framework.jar:/system/framework/ext.jar",
ClassLoader.getSystemClassLoader()
);
把framework.jar放在前面,因为90%的类都在这里面。这样能减少路径搜索时间。
4.4 实战避坑指南
说了这么多,最后分享几个我踩过的坑。
android.view.View,它一个类加载,会触发几十个相关类的加载。这种类,就算开机用不到,也别轻易删。删了反而得不偿失。
好了,Zygote优化的三个核心方向,就聊到这儿。下一章,咱们聊聊SystemServer的启动优化。那个坑更多,但优化空间也更大。