业务描述

核心功能模块:包括通知栏、超级岛、控制中心、锁屏、状态栏…等众多业务;

核心价值:提供统一的交互范式、核心功能快捷入口等系统可用性;

跟竞品的对比情况

如下是分类别的占比情况,排在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

SystemUI匿名页java堆内存分析

和竞品荣耀的差距:主要也是集中在AtomicRef、byte[]、String、以及各种原生容器的使用

和竞品荣耀magic 数据差距总表

测试场景:开机内存,16G root机型对比

机型对比:小米P1 vs 荣耀Magic8pro

总体内存差距:166M(166459KB)

当前内存工具拆解遇到的困难

showmap中汇总的数据和可分析的数据存在gap:

举例:

  1. 匿名页 java diff 12M,但是hprof统计出来对象差异只有8.3M;
  2. 匿名页 native, P1占用98.4M,但是Native堆拆解出来可分析的只有74M,存在25M左右不可分析的内存;

  1. 拿不到竞品荣耀 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

Systemui Java堆-Hprof对比分析

解决方案:通过下线多状态栏和懒加载相关图标的方式,优化内存,已有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

关键函数分析

  1. unknown (17.06%)

库: unknown

调用次数: 19,448

native大小: 3,225.04 KB

分析: 未知来源的内存分配,调用次数最多,平均每次分配0.166KB

  1. android::AssetManager2::GetBag (5.46%)

库: libandroidfw.so

调用次数: 590

native大小: 1,032.05 KB

分析: 资源包获取,平均每次分配1.75KB

  1. VectorDrawable::Path::Path (4.01%)

库: libhwui.so

调用次数: 3,421

native大小: 758.43 KB

分析: 矢量绘图路径创建,调用次数较多,平均每次分配0.222KB

  1. art::Thread::CreateNativeThread (3.78%)

库: libart.so

调用次数: 205

native大小: 714.23 KB

分析: ART线程创建,平均每次分配3.48KB

  1. 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的汇总数据同样存在差距;

SystemUI匿名页native堆内存分析 v2

<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 刘晓

  1. 非立即显示的Bitmap,考虑 lazy load 懒加载方式来创建
  2. 静态图片、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

解决方案:

  1. 主包以及两个插件包使用speed-profile的编译模式,覆盖高频热点类和方法 — @李金刚
  2. dagger非核心组件采用懒加载方式,减少代码访问量,在SystemUIInitializer的init方法里有添加代码的owner :@Anting Zhao 赵安廷@周众@罗然予@龙腾@Yu1 Cai 蔡宇@杨校春,需要排查各自对应逻辑;
  3. aod插件中,onetrack和flome动画延迟加载优化 — @鲜竞雄
  4. 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相关逻辑责任人竞品实现方式
超级岛sum2.8M
836https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5636524@安凤扬
2040https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/6458666@段春飞
通知sum0.3M
308https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/3090534@周众
控制中心
音量面板
妙播
sum10.4M
1924+212+512https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/4219344@罗然予
1520https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5767000@黄秀娥
6100https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5740798@李明雨
@黄秀娥
212https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5956436@罗然予
aodsum21.3M
21054AOD延迟加载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
@鲜竞雄
272https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUI/+/4975967@胡志超
@潘艳晓
sum1.2M
1232https://gerrit.pt.mioffice.cn/c/platform/packages/apps/MiuiSystemUIPlugin/+/5950139@罗然予

Graphic — 20.1M

SystemUI Graphics内存分析

  • GL_DEV — 2M

原因分析:P1 对字体进行全量记载,所以内存使用量较大

解决方案:使用按需加载的方式,优化内存;

  • EGL — 6.4M

原因分析:

P1采用双statusbar,导致开销大

解决方案:OS4 去掉冗余的状态栏;

  • GL — 11.6M

原因分析:

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