做客户端开发都基本都做过功用优化,比方提高自己所负责的事务的速度或流通性,优化内存占用等等。可是大部分开发者所做的功用优化或许都是针对中小型 APP 的,大型 APP 的功用优化经验并不会太多,毕竟大型 APP 就只有那么几个,什么是大型 APP 呢?以飞书来说,他的事务有 im,邮箱,日历,小程序,文档,视频会议……等等,包体积就有大几百 M,像这种事务十分多且杂乱的 APP 都能够认为是大型 APP。所以我在这篇文章主要讲一下大型 APP 是怎么做功用优化的,给大家在功用优化一块供给一个新的视角和启示。在这篇文章中,我主要会讲一下这两个主题:

  1. 大型 app 比较于中小型 app 在做功用优化时的异同点

  2. 大型 app 功用优化的思路

大型和小型运用功用优化的异同点

1.1 相同点

功用优化在实质上以及在优化维度上都是相同的。功用优化的实质是合理且充沛的运用硬件资源,让程序体现的更好;而且都需求依据运用层、体系层、硬件层三个维度来进行优化

大型 APP 的功用优化思路

1.2 不同点

针对体系层和硬件层的优化都是相同,有区别的主要是针对运用层的优化。

中小型 app 做事务和做功用优化的往往是同一个人,在做优化的时分,只需求考虑单个事务最优即可,咱们只需求给这些事务满足的硬件资源(比方给更多的内存资源:缓存更多的数据,给更多的 cpu 资源:用更多的线程来履行事务逻辑,给更多的磁盘资源:缓存满足的本地数据),而且合理的运用就能让事务体现的更好。只要这些单个的事务功用体现好,那么这款 app 的全体功用品质是不错

大型 APP 的功用优化思路

和中小型 APP 不同的是,大型 APP 事务多且杂乱,各个事务的团队很或许都不在一个部门,不在同一个城市。在这种状况下,假如每个事务也去追求自己事务的功用最优,同样是在自己的事务中运用更多的线程,运用更多的缓存,运用更多 cpu 的办法来使自己事务体现更好,那么就会导致 APP 全体的功用急剧劣化。因而大型 APP 需求有一个专门团队来做功用优化的,这个团队需求脱离某一个具体事务,站在大局的视角来让 APP 全体体现更优。

大型 APP 的功用优化思路

大型运用功用优化计划

总的来说由于资源是有限的,在中小型 APP 上,事务少,资源往往是满足的,咱们做功用优化时往往考虑的是怎么将资源充沛的发挥出来,而在大型 APP 上,资源往往是缺乏的,做功用优化时需求考虑即能充沛发挥硬件资源,又需求进行合理的分配,当咱们站在大局的视角来进行资源分配时,就需求考虑到这三个点:

  1. 怎么管控事务对资源的运用
  2. 怎么衡量事务对资源的耗费
  3. 怎么让事务在资源严重时做出更优的战略

下面我会针对速度及流通性优化、内存优化这两个方向,讲一讲针对这这三点的体现。

2.1 速度和流通性优化:怎么管控事务对资源的运用

在速度和流通性方向,中小型 APP 只需求分析和优化主途径的耗时逻辑;将同步使命尽量优化成异步使命;多进行预加载等计划,即能起很好的优化作用。可是关于大型 APP 来说,异步使命往往十分多,cpu 往往都是打满的状况,这种状况下主途径得不到满足的 cpu 资源,导致速度变慢。所以大型 app 中一般都会对事务的异步使命,如发动阶段的预加载进行管控,因而需求预加载结构或许相似的结构,来收敛、管控、以及调度一切事务的预加载使命。咱们来看一下在大型 APP 中,通用的预加载结构是怎么做的。

2.1.1 预加载结构

想要管控事务的预加载使命,咱们需求考虑这两个点:

  1. 预加载使命的增加办法

  2. 预加载使命调度和办理的机制

  3. 预加载使命的增加办法

首要要将事务的预加载使命和事务进行解耦,要能做到即使该预加载使命不履行,也不会影响到事务的正常运用,而且将预加载使命封装成粒度最小的 task,然后直接将这些 task 丢到到预加载结构中,咱们能够经过单例供给一个 addPreloadTask 办法,事务方只需求调用该接口,并传入预加载使命 task 以及一些属性及配置参数即可。将预加载使命增加到预加载结构后,事务方就不需求进行任何其他操作了,是否履行、什么时分履行,都交给预加载结构来办理。

大型 APP 的功用优化思路

  1. 预加载使命调度机遇

那么预加载结构关于增加进来的 task 怎么调度呢?这便是一个预加载结构杂乱的的当地的,咱们能够有很多战略,比方这三种:

  1. 关键节点调度战略:比方各个生命周期阶段,页面烘托完成阶段等去履行,也能够在使命增加进来后马上履行。
  2. 功用调度战略:比方判别 cpu 是否忙碌,温度是否过高,内存是否满足等,只有在功用较好的状况下才进行调度
  3. 用户行为调度战略:假如做的更杂乱一些,还能够结合用户的行为目标,如该事务用户是否会运用,假如某一个用户从来不适用这个 app 里的这个功用,那么改事务增加进来的预加载使命就能够完全舍弃到,这儿边能够用一些端智能的计划来精细化的控制预加载使命的调度

每种调度战略不是单独履行的,咱们能够将各种战略整合起来,构成一套完善的调度战略。

大型 APP 的功用优化思路

2.2速度和流通性优化:怎么让事务在资源严重时做出更优的战略

上面说到的是站在大局的视角,怎么管控预加载使命的,除了预加载使命,还有很多其他的异步使命咱们都能够用一些结构来规范化的管控起来,这儿再举一个比方,关于大型 APP 来说,事务在运用的过程中很容易呈现由于 cpu 或内存缺乏导致卡顿,呼应慢等功用问题,所以在做功用优化时,是需求推进事务方在资源缺乏时,做出相应战略的,这个时分咱们就需求降级结构来处理了。降级结构需求处理这两个问题:

  1. 功用目标的收集
  2. 降级使命的调度

2.2.1 降级结构

  1. 功用目标的收集

想要再资源严重时让事务做出优化战略,那么对资源严重的判别便是必不可少的一步。咱们一般经过在程序运转过程中,收集设备功用目标来判别资源是否严重,最基本的功用目标有 cpu 运用率,温度,Java 内存,机型等,除机型外其他功用目标一般都是以固定的频率进行收集,如 cpu 运用率能够 10s 收集一次,温度能够 30s 收集一次,java 内存能够 1 分钟收集一次,收集的频率需求考虑对功用的影响以及目标的敏感度,比方 cpu 的运用率收集,需求读取 proc/stat 的文件并解析,是有一定功用损耗的,所以咱们在收集时,不能太频繁;温度的改变是比较慢的,咱们收集的频率也能够长一些。降级结构需求整合这些功用目标的收集,削减各个事务自己收集造成不必要的功用损耗。

当降级结构收集到功用目标,并判别当时资源反常时,通用的做法是告诉各个事务,事务收到告诉后再进行降级。比方体系的 lowmemorykiller 机制,都是采用告诉的办法。

大型 APP 的功用优化思路

可是在大型 APP 中,仅仅将触发功用阈值的告诉给到各个事务方,作用不会太好,由于事务方或许并不会去呼应告诉,或许个别事务呼应了,可是其他事务不呼应,仍然作用欠安。无法管控事务是否进行降级,这显然不符合在大型 APP 做功用优化的思路,那么咱们要怎么做呢?

  1. 降级使命的调度

增加使命:咱们仍然能够推进各个事务将降级的逻辑封装在 task 中,而且注册到降级结构中,并由降级结构来进行调度和办理。由于往降级结构注册 task 时,需求带上事务的称号,所以咱们能也能清楚的知道,那些事务有降级处理逻辑,哪些事务没有,关于没有注册的事务,需求专门推进进行降级呼应以及 task 的注册。

调度使命:和预加载结构相同,关于注册进来的 task,降级结构的使命调度要考虑清楚调度的机遇,以 cpu 运用率为例,不同的设备下的阈值也是不相同的,高端机型或许 cpu 的运用率在 70%以上,app 仍是流通的,可是低端机在 50%以上就开端卡顿了,因而不同的机型需求依据经验值或许线上数据设置一个合理的阈值。当 cpu 抵达这个阈值时,降级结构便开端履行注册到 cpu 列表中的降级使命,在履行降级使命时,不需求将队列里的 task 全部履行,咱们能够分批履行,假如履行到某一批降级 task 时,cpu 康复到阈值以下了,后边的降级 task 就能够不用在履行了。能够看到,经过降级结构,咱们就能够站在大局的维度,去进行更好的管控,比方咱们能够衡量事务做降级使命的作用,给到一个评分,关于作用欠好的,能够推进优化。

大型 APP 的功用优化思路

2.3 内存优化:怎么衡量事务对资源的耗费

上面两个比方将的是在大型 app 中,怎么管控事务对资源的运用,以及怎么让事务在资源严重时做出更优的战略的思路,我接着依据内存优化的方向,讲一讲怎么衡量事务对资源的耗费。

当 app 运转过程中,往往只能获得全体的内存的数据占用,没法获的各个事务耗费了多少内存的,由于各个事务的数据都是放在同一个堆中的,关于小型 app 来说这种状况并不是问题,由于就那么几个事务在运用内存,可是关于大型 app 来说便是一个问题了,有些事务为了自己功用目标能更好,会占用更多的内存,导致全体的内存占用过高。所以咱们需求弄清每个事务究竟运用了多少内存才能推进事务进行优化。

大型 APP 的功用优化思路

咱们能够线下经过分析 hprof 文件或许其他调试的办法来弄清楚每个 app 的内存占用,可是很多时分没有满足的时间在版别都去计算一下,或许即使计算了,也或许由于途径没掩盖全导致数据不准确。所以咱们最好能经过线上监控的办法,就能计算到事务的内存耗费,而且在内存耗费反常的时分进行上报。

我在这儿介绍一种思路。大部分的事务都是以 activity 呈现的,所以咱们能够监听大局的 activity 创立,在事务的 onCreate 最前面计算一下 java 和 native 内存的大小,作为这个事务发动时的基准内存。然后在 acitvity 运转过程中,固定收集在当时 activity 下的内存并减去 onCreate 时的基准内存,咱们就能衡量出当时事务的一个内存耗费状况了。在该 acitvity 完毕后,咱们能够自动触发一下 gc,然后在和前面的基准内存 diff 一下,也能计算出该事务完毕后的增量内存,理想状况下,增量内存应该是要小于零的,由于 gc 需求 cpu 资源,所以咱们只需求开取小部分的采样率即可。

大型 APP 的功用优化思路

当咱们能在运转过程中,计算各个事务的内存耗费,那么就能够推进内存耗费高的事务进行优化,或许当某个版别的某个事务呈现较大的劣化时,触发报警等。

除了上面说到的思路,咱们也能够计算在事务运用过程中的触顶次数,计算出一个触顶率的目标,触顶及 java 内存占用达到一个阈值,比方 80%,咱们就能够认为触顶了,关于触顶次数高的事务,同样也能够进行反常上报,然后推进事务方进行修正。这些数据和目标的计算,都是无侵入的,所以并不需求咱们了解事务的细节。

假如咱们想做的更细一些,还能够 hook 图片的创立,hook 集合的 add,remove 等办法,当监控到大图片和大集合时,打印仓库,并将关键信息上报。在飞书,假如是低端机中,图片假如占用内存过大的,都会在 hook 办法中进行一些紧缩或许降低质量的兜底处理。

总结

除了速度及流通性,内存方向的优化外,还有其他方向的优化,如包体积,稳定性,功耗等,在大型 APP 上都要依据管控事务对资源的运用;衡量事务对资源的耗费;让事务在资源严重时做出更优的战略这三个方向去进行优化,这儿我就不再一一打开讲了。

大型 APP 的功用优化思路

当然我这儿讲的优化思路并不是大型 app 做功用优化的全部,我讲的只是在做大型 app 的功用时比较于中小型 app 需求额外做的,而且也是作用最好的优化,这些计划在中小型 app 上或许并不需求。除了我这篇文章讲的内容外,还有很多优化的计划,这些计划不管是在大型 app 仍是中小型 app 上都是通用的,比方深入了解事务,依据事务逻辑去做分析和优化,抓 trace,分析 trace 等等,或许依据体系层或许硬件层去做一些优化等等,这儿就不再打开讲了。