业务描述
核心功能模块:包括通知栏、超级岛、控制中心、锁屏、状态栏…等众多业务;
核心价值:提供统一的交互范式、核心功能快捷入口等系统可用性;
跟竞品的对比情况

如下是分类别的占比情况,排在TOP3的是文件页、匿名页-Native和匿名页-Java.其中文件页内存占比相差120M

文件页内存细分:
其中相差最大的依次是dex、apk、vdex、so等

P1 文件页占用排序:

荣耀magic8 pro

匿名页内存细分
匿名页 Native — diff 31.7M
如下是申请内存的top10调用库:其中最大头的是调用libhwui.so的内存申请函数,占45M
内存申请函数的大于100K如下,按照内存size 排序:
按照 P1 count排序:
匿名页 java — diff 8.3M
为什么不用showmap的diff? showmap中统计规则有问题,后续会和meminfo中的java heap大致保持一致,最终使用hprof分析。Hprof统计到的堆内存,P1 57.6M/magic 49.3M,相差约8.3M
和竞品荣耀的差距:主要也是集中在AtomicRef、byte[]、String、以及各种原生容器的使用

和竞品荣耀magic 数据差距总表
测试场景:开机内存,16G root机型对比
机型对比:小米P1 vs 荣耀Magic8pro
总体内存差距:166M(166459KB)
当前内存工具拆解遇到的困难
showmap中汇总的数据和可分析的数据存在gap:
举例:
- 匿名页 java diff 12M,但是hprof统计出来对象差异只有8.3M;
- 匿名页 native, P1占用98.4M,但是Native堆拆解出来可分析的只有74M,存在25M左右不可分析的内存;

- 拿不到竞品荣耀 magic8pro的详细filemap 信息,无法进一步对比差异点;
systemui内存分解
<chart_refer_host_perm token=“chtcnpohP2rnNuBu6YWhx7Jp0ed”></chart_refer_host_perm>
内存细分:
代码段

- 主包systemui speed 编译,并且线上版本已经开启ProGuard混淆优化;
- 友商的编译模式: speed - profile
匿名页-java - 37.8M
现阶段已经优化6.5M,和挑战值还有11.5M的差距:
可能需要从如下内存占用大头优化,但下面这些对象内存占用和竞品差异并不大!大部分都是必要的内存开销;
byte[] — 17.1M
java.lang.Class — 5.5M
java.lang.String — 3.6M
java.lang.Object[] — 2.8M
long[] — 2.8M
kotlinx.atomicfu.AtomicRef — 2.8M
…
Java heap count分解
主要集中在AtomicRef、byte[]、String、以及各种原生容器的使用
<chart_refer_host_perm token=“chtcn736K4krc7A8E3jB11cXVEb”></chart_refer_host_perm>
Java heap shallow分解,同样是原生类的使用
如下是P1 top 50 java heap的排序

目前各个对象指向的根节点都比较分散,涉及不同的业务,需要内存占用从多到少逐个排查:
举例:
kotlinx.atomicfu.AtomicRef — 2.8M
Systemui hprof分析实操(kotlinx.atomicfu.AtomicRef)
原因分析:

通过合并引用链,基本都是类似的引用结构,dagger单例,每个provider持有多个ReadonlyStateFlow实例;
解决方案:dagger架构,需要长期优化;
systemui业务强相关对象拆解
强相关对象java heap占用排序:

业务强相关对象,700+K,com.android.systemui.statusbar.StatusBarIconView 188K和com.android.systemui.log.LogMessageImpl 148K

解决方案:通过下线多状态栏和懒加载相关图标的方式,优化内存,已有AR 跟踪 — @黄前方
从业务角度逐步优化中,优化深水区!需要长期一点点扣内存
音量面板:

通知栏:
超级岛:
控制中心:
锁屏:
Java 堆函数级拆解

原因分析:
1.更多的view进行初始化的操作

大比例的类加载相关操作,由于注入依赖框架Dagger的使用,编译阶段会生成大量辅助类,它会加载辅助类的依赖链上的所有类,再配合Speed编译模式,小米 systemui 应用在初始化时加载(类查找+ 类加载 行为)了所有dagger生成的辅助类与其依赖类(对于odex文件的数据访问,占整个文件大小的90+%)
解决方案:systemui的主包和插件包plugin+aod均同样采用speed-profile的编译模式 — @李金刚
规划OS4 落地 systemui主包的 speed-profile编译;
匿名页-native — 98.4M
现阶段已经优化21.9M,和挑战值还有24.8M的差距:
内存占用TOP9已经拆解完,剩余内存占用占用低,优化效果不明显

对剩余未拆解函数top60聚类:
Top 60总大小18.9M


关键函数分析
- unknown (17.06%)
库: unknown
调用次数: 19,448
native大小: 3,225.04 KB
分析: 未知来源的内存分配,调用次数最多,平均每次分配0.166KB
- android::AssetManager2::GetBag (5.46%)
库: libandroidfw.so
调用次数: 590
native大小: 1,032.05 KB
分析: 资源包获取,平均每次分配1.75KB
- VectorDrawable::Path::Path (4.01%)
库: libhwui.so
调用次数: 3,421
native大小: 758.43 KB
分析: 矢量绘图路径创建,调用次数较多,平均每次分配0.222KB
- art::Thread::CreateNativeThread (3.78%)
库: libart.so
调用次数: 205
native大小: 714.23 KB
分析: ART线程创建,平均每次分配3.48KB
- RecordingCanvas::onDrawDrawable (3.62%)
库: libhwui.so
调用次数: 171
native大小: 684.00 KB
分析: 录制画布绘制可绘制对象,平均每次分配4.00KB
- unknown内存分配成为最大的单一占用项,占总native大小的17.06%,需要优先排查
- AssetManager2::GetBag占用5.46%,涉及资源包管理,优化空间较大
- VectorDrawable::Path::Path占用4.01%,调用3,421次,是高频调用的UI渲染函数
- 高频调用的函数(如ShaderCache、String8)虽然单次占用小,但累积效应明显
内存工具解析后,可分析的native P1 74M,magic8 44.8M; 标准值 差距 29.2M,挑战值 差距 38.1M,和showmap的汇总数据同样存在差距;
<chart_refer_host_perm token=“chtcn3CX7O82M1UjXN95fHUlPNb”></chart_refer_host_perm>
ShaderCache — 24.84M
qglinternal::vkCreatePipelineCache (11.76M)
android::BlobCache::set (11.1M)
android::BlobCache::Blob::Blob (1.98M)
总计:24.84 M
和magic 8 差距21.94M
原因:这3个是 Shader Cache相关,根本原因是小米SystemUI中使用了布局层级更为复杂的圆角嵌套效果,导致 ClipStack 更深,Shader组合效果(级联)更多,进而产生更多的 ShaderCache。
解决方案:目前有一版相关的针对性优化方案正在跟进中,预计优化70%~80% (16~18MB) — @王月
Bitmap创建 — 12.7M
与竞品差距5.6M
getAppIcon相关 — 1.9M
#27: com.miui.systemui.graphics.AppIconsManager.getAppIconInner [/data/dalvik-cache/arm64/system_ext@priv-app@MiuiSystemUI@MiuiSystemUI.apk@classes.dex]

KeyguardNotificationController创建bitmap — 0.65M

较大需要排查是否可以优化的bitmap,top10,总5.5M:
- 通知icon 大图1 — (*2)492K*2
- 通知icon 大图2 — (*2)492K*2
- 锁屏大图,367KB
- 锁屏大图,277KB
- 通知大图,274KB
- 通知图标,219KB
- 通知大图,219KB
- 通知大图,219KB
- 锁屏大图,219KB — (*5) 219*5
- 通知大图,219KB — (*4) 219*4
主要涉及锁屏和通知业务中bitmap的创建
解决方案:@胡志超@Xiao11 Liu 刘晓
- 非立即显示的Bitmap,考虑 lazy load 懒加载方式来创建
- 静态图片、Icon图标类图片,考虑设计上降低分辨率规格
RenderNode — 2.02M
android::uirenderer::RenderNode::RenderNode
原因分析:由于dagger框架的使用 + Speed模式,引起几乎全量(90+%,根据dex文件数据访问火焰图)的预加载方案,SystemUI创建并并缓存更多的View对象,涉及: 小窗、通知、锁屏、状态栏、控制中心等各模块;
解决方案:
1.将systemui编译模式改成speed-profile优化;
2.和业务沟通,非必要view使用懒加载的形式;
文字绘制 — 6.2M
sk_malloc_flags — 4.1M
GrDrawOpAtlas::addToAtlas — 0.7M
SkArenaAlloc::ensureSpace — 1.4M

原因分析:
- text等初始化
- canvas.dawText \ canvas.drawTextRun \ canvas.drawTextOnPath 等文字绘制API 调用触发。
SkArenaAlloc, 是Skia 中的一个 arena / bump-pointer 内存分配器,用于存储 绘制过程中的临时对象
解决方案:SystemUI创建并并缓存更多的TextView对象,涉及: 小窗、通知、锁屏、状态栏、控制中心等各模块,需要逐步优化;
VectorDrawable — 758K
android::uirenderer::VectorDrawable::Path::Path (diff = 758KB )
原因分析:小米的 SystemUI 中部分UI布局使用了SVG,包括控制中心媒体栏、音量条、状态栏图标等。为了启动速度优化,进行了UI的预加载处理,例如控制中心。因此在开机启动场景下,部分View,即使没有显示出来,也会提前创建(Inflate)。反观竞品,UI布局中未使用任何的 VectorDrawable。
解决方案:需要调研SVG 的代替方案;
文件页 — 211.7M
现阶段已经优化89M,和挑战值还有47.7M的差距:
详细差距如下:
插件包aod和plugin 文件页涉及的详细火焰图看 3.4.6节!
<chart_refer_host_perm token=“chtcn4LZbNu15eoy1fA3RWBeko8”></chart_refer_host_perm>
Dex mmap
和竞品存在93.6M的差距,竞品不存在aod和plugin这两个插件的dex
<chart_refer_host_perm token=“chtcnTaD5GQxLXIJsSSKx1clDnd”></chart_refer_host_perm>
现状:
主要涉及主包 systemui和插件plugin和aod的dex和vdex
原因分析:
主包使用speed编译模式,导致引入大量dex mmap
mi@tao:~$ adb shell pm art dump com.android.systemui
[com.android.systemui]
path: /system_ext/priv-app/MiuiSystemUI/MiuiSystemUI.apk
arm64: [status=speed] [reason=bg-dexopt] [primary-abi]
[location is /data/dalvik-cache/arm64/system_ext@priv-app@MiuiSystemUI@MiuiSystemUI.apk@classes.dex]
mi@tao:~$ adb shell pm art dump miui.systemui.plugin
[miui.systemui.plugin]
path: /data/app/~~GHy5pb9ONHetnSWfStqPjw==/miui.systemui.plugin-Q50d0y_HGR_hZgbXwCehow==/base.apk
arm64: [status=verify] [reason=install] [primary-abi]
[location is /data/app/~~GHy5pb9ONHetnSWfStqPjw==/miui.systemui.plugin-Q50d0y_HGR_hZgbXwCehow==/oat/arm64/base.odex]
used by other apps: [com.android.systemui (isa=arm64)]
mi@tao:~$ adb shell pm art dump com.miui.aod
[com.miui.aod]
path: /data/app/~~CzqaQ6aWY2Bs6Q9SYXZflQ==/com.miui.aod-X58LoQtGLDY9n835htgxCw==/base.apk
arm64: [status=verify] [reason=install] [primary-abi]
[location is /data/app/~~CzqaQ6aWY2Bs6Q9SYXZflQ==/com.miui.aod-X58LoQtGLDY9n835htgxCw==/oat/arm64/base.odex]
used by other apps: [com.android.systemui (isa=arm64)]
systemui — 68.1M:
- systemui引入dagger框架,它会遍历依赖树,加载解析几乎所有代码,使用speed-profile编译可以优化50M,优于竞品的主包dex mmap;
在SystemUIInitializer的init方法里,和pixel对比差异比较大,锁屏、通知、多任务等模块在init增加初始化逻辑;

aod — 22.7M:
- 因为com.miui.keyguard.shortcuts.ShortcutPluginImpl.onCreate — 锁屏快捷方式,21M

解决方案:
剥离aod插件,部分业务移回systemui
plugin — 26.6M:
- 音量面板 4.1M

解决方案:
- 主包以及两个插件包使用speed-profile的编译模式,覆盖高频热点类和方法 — @李金刚
- dagger非核心组件采用懒加载方式,减少代码访问量,在SystemUIInitializer的init方法里有添加代码的owner :@Anting Zhao 赵安廷@周众@罗然予@龙腾@Yu1 Cai 蔡宇@杨校春,需要排查各自对应逻辑;
- aod插件中,onetrack和flome动画延迟加载优化 — @鲜竞雄
- plugin插件中,音量面板flome动画延迟加载优化 — @黄秀娥
Apk mmap
和竞品存在29.5M的差距,竞品不存在aod和plugin这两个插件的apk
<chart_refer_host_perm token=“chtcnv8xGUuZYvbLxTSESmFFZpe”></chart_refer_host_perm>
MiuiSystemUI.apk — 33.9M


原因分析:
主要还是dagger引入的热点函数,同样在com.android.systemui.SystemUIInitializer.init中有添加新逻辑;
解决方案:同dagger非核心组件采用懒加载方式中解决方法;
MIUIAod.apk —7.6M

原因分析:
1.com.android.systemui.shared.plugins.PluginInstance$PluginFactory.createPluginContext — 7M
createApplicationContext 引入内存增量

解决方案:原生插件context创建逻辑逻辑,aod作为systemui插件
剥离aod插件,部分业务移回systemui
MIUISystemUIPlugin.apk — 20.5M

原因分析:
1.com.android.systemui.shared.plugins.PluginInstance$PluginFactory.createPlugin和miui.systemui.plugins.PluginBase.onCreate 初始化 — 原生逻辑,plugin加载逻辑;
2.超级岛、音量面板、AutoDensityController等业务的初始化;
解决方案:超级岛、音量面板、AutoDensityController等业务非开机必须的初始化使用懒加载形式;
MISettings.apk — 4.4M
可以通过拉齐测试手法优化
开机未成年人模式通知引入setting apk,竞品应该已经点击了不再提示,这个界面测试前需要对齐

竞品 12G Apk mmap回收策略补充
补充 :12G竞品荣耀开机初始apk mmap实际有50+M,和P1的差距是10+M ,但是过几分钟就会变成几百K,内核分析回收策略差异
So mmap — 14.4M
<chart_refer_host_perm token=“chtcn5kbleOPvNi2WhFJLoaQC0c”></chart_refer_host_perm>
原因分析
/vendor/lib64/libllvm-qgl.so — 9.0M
同样涉及圆角相关的shader编译,qgl.so主要依赖业务侧针对圆角的优化
解决方案:主要涉及业务:通知中心、控制中心。目前圆角方案为View.setClipToOutline 和 canvas.clipPath 居多,这种方式存在性能与内存问题,现需要修改为drawPath的方式@Xiao11 Liu 刘晓@Jeno Sun 孙健豪
Ttf mmap — 5.1M
解决方案:精简字体库、懒加载字体库等手段,优化字体文件在应用内存中的占用 @李昆鸿
AOD 和 plugin中初始化涉及的模块
- 需要代码owenr进一步排查自己写的逻辑是否有必要在开机时就加载
| 业务 | filemap | 文件页大小-KB | 相关逻辑 | 责任人 | 竞品实现方式 |
| 超级岛 | sum | 2.8M | |||
| 836 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5636524 | @安凤扬 | |||
| 2040 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/6458666 | @段春飞 | |||
| 通知 | sum | 0.3M | |||
| 308 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/3090534 | @周众 | |||
| 控制中心 音量面板 妙播 | sum | 10.4M | |||
| 1924+212+512 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/4219344 | @罗然予 | |||
| 1520 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5767000 | @黄秀娥 | |||
| 6100 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5740798 | @李明雨 @黄秀娥 | |||
| 212 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5956436 | @罗然予 | |||
| aod | sum | 21.3M | |||
| 21054 | AOD延迟加载oneTrack: https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiAod/+/7004495 延迟加载Folme动画相关类: https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiAod/+/7006860 去除主包中不用的view https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUI/+/7015215 | @鲜竞雄 | |||
| 272 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUI/+/4975967 | @胡志超 @潘艳晓 | |||
| sum | 1.2M | ||||
| 1232 | https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5950139 | @罗然予 |
Graphic — 20.1M
- GL_DEV — 2M

原因分析:P1 对字体进行全量记载,所以内存使用量较大
解决方案:使用按需加载的方式,优化内存;
- EGL — 6.4M

原因分析:
P1采用双statusbar,导致开销大
解决方案:OS4 去掉冗余的状态栏;
- GL — 11.6M

原因分析:
P1 中的RenderTarget 占用6.29M,存在2048x2048的打规格资源,图形加速字体渲染集合;