1 前语

发动是App给用户的榜首印象,一款App的发动速度,不单单是用户体验的作业,往往还决议了它能否获取更多的用户。所以到了必定阶段App的发动优化是有必要要做的作业。App发动根本分为以下两种

1.1 冷发动

App 点击发动前,它的进程不在体系里,需求体系新创立一个进程分配给它发动的情况。这是一次完好的发动进程。

体现:App榜首次发动,重启,更新等

1.2 热发动

App 在冷发动后用户将 App 退后台,在 App 的进程还在体系里的情况下,用户从头发动进入 App 的进程,这个进程做的作业十分少。

所以咱们首要说道说道冷发动的优化

2 发动流程

2.1 APP发动都干了什么

要对发动速度进行优化,咱们需求知道发动进程中的大致流程是什么,做了什么作业,是否能针对性优化。
下图是发动流程的详细分解

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

  1. 点击图标,创立进程
  2. mmap 主二进制,找到 dyld 的途径
  3. mmap dyld,把入口地址设为_dyld_start

dyld 是发动的辅助程序,是 in-process 的,即发动的时分会把 dyld 加载到进程的地址空间里,然后把后续的发动进程交给 dyld。dyld 首要有两个版别:dyld2 和 dyld3。

iOS 12之前首要是dyld2,iOS 13 开端 Apple 对三方 App 启用了 dyld3,dyld3 的最重要的特性便是发动闭包,闭包存储在沙盒的 tmp/com.apple.dyld 目录,整理缓存的时分牢记不要整理这个目录。

闭包里首要有以下内容:

  • dependends,依靠动态库列表
  • fixup:bind & rebase 的地址
  • initializer-order:初始化调用次序
  • optimizeObjc: Objective C 的元数据
  • 其他:main entry, uuid等等

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

上图虚线之上的部分是out-of-process的,在App下载安装和版别更新的时分会去履行,直接从缓存中读取数据,加快加载速度

这些信息是每次发动都需求的,把信息存储到一个缓存文件就能避免每次都解析,尤其是 Objective-C 的运转时数据(Class/Method…)解析耗时, 所以对发动速度是一个优化提升

4.把没有加载的动态库 mmap 进来,动态库的数量会影响这个阶段

dyld从主履行文件的header获取到需求加载的所依靠动态库列表,然后它需求找到每个 dylib,而运用所依靠的 dylib 文件或许会再依靠其他 dylib,所以所需求加载的是动态库列表一个递归依靠的调集

5.对动态库调集循环load, mmap 加载到虚拟内存里,对每个 Mach-O 做 fixup,包含 Rebase 和 Bind。

对每个二进制做 bind 和 rebase,首要耗时在 Page In,影响 Page In 数量的是 objc 的元数据

  • Rebase 在Image内部调整指针的指向。在过去,会把动态库加载到指定地址,一切指针和数据关于代码都是对的,而现在地址空间布局是随机化(ASLR),所以需求在原来的地址依据随机的偏移量做一下批改, 也便是说Mach-O 在 mmap 到虚拟内存的时分,开始地址会有一个随机的偏移量 slide,需求把内部的指针指向加上这个 slide.
  • Bind 是把指针正确地指向Image外部的内容。这些指向外部的指针被符号(symbol)称号绑定,dyld需求去符号表里查找,找到symbol对应的完结, 像 printf 等外部函数,只要运转时才知道它的地址是什么,bind 便是把指针指向这个地址,这也是后边咱们能用fishhook来hook一些动态符号的中心

如下图,编译的时分,字符串 1234 在__cstring的 0x10 处,所以 DATA 段的指针指向 0x10。可是 mmap 之后有一个偏移量 slide=0x1000,这时分字符串在运转时的地址便是 0x1010,那么 DATA 段的指针指向就不对了。Rebase 的进程便是把指针从 0x10,加上 slide 变成 0x1010。运转时类方针的地址现已知道了,bind 便是把 isa 指向实践的内存地址。

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

6.初始化 objc 的 runtime,因为闭包现已初始化了大部分,这儿只会注册 sel 和装载 category

7.+load 和静态初始化被调用,除了办法自身耗时,这儿或许还会引起许多 Page In,假如调用了dispatch_async则会延迟发动后的runloop开启后履行,假如触发静态初始化,则会延迟到运转时履行

8.初始化 UIApplication,发动 Main Runloop,能够在之前章节运用runloop核算首屏耗时,也能够在发动完毕做一些预热使命

9.履行 will/didFinishLaunch,这儿首要是事务代码耗时。主页的事务代码都是要在这个阶段,也便是首屏烘托前履行的,首要包含了:首屏初始化所需装备文件的读写操作;首屏列表大数据的读取;首屏烘托的许多核算等;sdk的初始化;关于大型组件化工程,也包含了许多moudle的发动加载项

10.Layout,viewDidLoad 和Layoutsubviews 会在这儿调用,Autolayout 太多会影响这部分时刻

11.Display,drawRect 会调用

12.Prepare,图片解码产生在这一步

13.Commit,首帧烘托数据打包发给 RenderServer,走GPU烘托流水线流程,发动完毕

(tips: 2.2.10-2.2.13这儿首要是图形烘托流水线的部分流程,Application产生图元阶段(CPU阶段))。后续会交由单独的RenderServer进程,再调用烘托结构(Metal/OpenGL ES)来生成 bitmap,放到帧缓冲区里,硬件依据时钟信号读取帧缓冲区内容,完结屏幕改写

2.2 发动各阶段时长核算

上一小节对发动各个阶段进程的详细论述,归纳起来大致分为6个阶段(WWDC2019):

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

经过对各个阶段进行时长核算剖析,进行优化然后比照。

能够在Xcode中设置环境变量DYLD_PRINT_STATISTICS和DYLD_PRINT_STATISTICS_DETAILS看下发动阶段和对应的耗时(iOS15后环境变量失效)

也能够经过Xcode MetricKit 自身也能够看到发动耗时:翻开 Xcode -> Window -> Origanizer -> Launch Time

假如公司有对应的老练监控体系最好,这儿咱们首要经过手动无侵入埋点去核算发动时长,对发动流程pre main-> after main进行核算剖析

2.1.1 进程创立时刻打点

经过 sysctl 体系调用拿到进程创立的时刻戳

#import <sys/sysctl.h>
#import <mach/mach.h>
+ (BOOL)processInfoForPID:(int)pid procInfo:(struct kinfo_proc*)procInfo
{
    int cmd[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    size_t size = sizeof(*procInfo);
    return sysctl(cmd, sizeof(cmd)/sizeof(*cmd), procInfo, &size, NULL, 0) == 0;
}
+ (NSTimeInterval)processStartTime
{
    struct kinfo_proc kProcInfo;
    if ([self processInfoForPID:[[NSProcessInfo processInfo] processIdentifier] procInfo:&kProcInfo]) {
        return kProcInfo.kp_proc.p_un.__p_starttime.tv_sec * 1000.0 + kProcInfo.kp_proc.p_un.__p_starttime.tv_usec / 1000.0;
    } else {
        NSAssert(NO, @"无法取得进程的信息");
        return 0;
    }

2.1.2 main()履行时刻打点

// main之前调用
// pre-main()阶段完毕时刻点:__t2
void static __attribute__ ((constructor)) before_main()
{
  if (__t2 == 0)
  {
    __t2 = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970;
  }
}

2.1.3 首屏烘托时刻打点

发动的终点对运用户感知到的 Launch Image 消失的榜首帧

iOS 12 及以下:root viewController 的 viewDidAppear

iOS 13+:applicationDidBecomeActive

Apple 官方的核算办法是榜首个 CA::Transaction::commit,但对应的完结在体系结构内部,不过咱们能够找到最接近这个的时刻点

经过 Runloop 源码剖析和调试,咱们发现 CFRunLoopPerformBlock,kCFRunLoopBeforeTimers 和 CA::Transaction::commit()为最近的时刻点,所以在这儿打点即可.

详细便是能够经过在 didFinishLaunch 中向 Runloop 注册 block 或许 BeforeTimer 的 Observer 来获取这两个时刻点的回调,代码如下:

注册block:

//注册block
CFRunLoopRef mainRunloop = [[NSRunLoop mainRunLoop] getCFRunLoop];
CFRunLoopPerformBlock(mainRunloop,NSDefaultRunLoopMode,^(){
    NSTimeInterval stamp = [[NSDate date] timeIntervalSince1970];
    NSLog(@"runloop block launch end:%f",stamp);
});

监听BeforeTimer 的 Observer

//注册kCFRunLoopBeforeTimers回调
CFRunLoopRef mainRunloop = [[NSRunLoop mainRunLoop] getCFRunLoop];
CFRunLoopActivity activities = kCFRunLoopAllActivities;
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, activities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    if (activity == kCFRunLoopBeforeTimers) {
        NSTimeInterval stamp = [[NSDate date] timeIntervalSince1970];
        NSLog(@"runloop beforetimers launch end:%f",stamp);
        CFRunLoopRemoveObserver(mainRunloop, observer, kCFRunLoopCommonModes);
    }
});
CFRunLoopAddObserver(mainRunloop, observer, kCFRunLoopCommonModes);

综上剖析现有项目版别发动时刻均值:

[函数名:+[LaunchTrace mark]_block_invoke][行号:54]—————App发动————-耗时:pre-main:4.147820
[函数名:+[LaunchTrace mark]_block_invoke][行号:55]—————App发动————-耗时:didfinish:0.654687
[函数名:+[LaunchTrace mark]_block_invoke][行号:56]—————App发动————-耗时:total:4.802507

3 发动优化

上节咱们首要剖析了App发动流程和时长核算,下面便是咱们要优化的方向,尽或许对各个阶段进行优化,当然也不是过度优化,项目不同阶段、不同规模相应的问题会不一样,做针对性剖析优化.

3.1 Pre Main 优化

3.1.1 调整动态库

查看了现有工程,根本都以动态库进行链接,总计48个,所以思路如下

  • 削减动态库,自有动态库转静态库
  • 现有的库是以CocoaPods办理的,所以经过hook pod构建流程修正Xcode config将部分pod的Mach-O type改为Static Library;
  • 一起对一些代码较大的动态库进行ROI剖析,剖析是否能够不依靠,在代码内即可完结替代逻辑,这样删去一些ROI很低的动态库
  • 合并动态库
  • 目前项目引入的动态库较为简单,不存在合并项,关于有些中大型工程,有许多自己的基建UI库,许多过于分散,需求做的便是能聚合就聚合,比如XXTableView, XXHUD, XXLabel,主张合并成一个XXUIKit;比如一些东西库,也能够依据实践情况聚合为一个
  • 动态库懒加载
  • 经过剖析目前项目阶段规模还没必要进行懒加载动态库,究竟优化要考虑收益,仅做优化思路参阅
  • 正常动态库都是会被主二进制直接或许间接链接的,那么这些动态库会在发动的时分加载。假如只打包进 App,不参加链接,那么发动的时分就不会主动加载,在运转时需求用到动态库里边的内容的时分,再手动懒加载
  • 运转时经过-[NSBundle load]来加载,本质上调用的是底层的 dlopen。

3.1.2 rebase&binding&objc setup阶段

  • 无关的Class、Method的符号加载耗时也会带来额定的发动耗时;所以咱们要削减__DATA段中的指针数量;对项目代码剖析发现许多相似的Category,每个Category里边或许只要一个功用函数,所以详细依据项目情况剖析进行Category合并

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

  • +load 除了办法自身的耗时,还会引起许多 Page In,别的 +load 的存在对 App 稳定性也是冲击,因为 Crash 了捕获不到。
  • 项目中不少相似以下load函数逻辑,详细剖析后许多能够作为发动器进行治理办理,runloop空闲去履行,
  • 首屏后延时加载

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

  • 别的一类是load逻辑操作:许多组件化通讯解耦计划之一便是在load函数内做协议和类的绑定,这部分能够运用 clang attribute,将其迁移到编译期:
typedef struct{
    const char * cls;
    const char * protocol;
}_di_pair;
#if DEBUG
#define DI_SERVICE(PROTOCOL_NAME,CLASS_NAME)\
__used static Class<PROTOCOL_NAME> _DI_VALID_METHOD(void){\
    return [CLASS_NAME class];\
}\
__attribute((used, section(_DI_SEGMENT "," _DI_SECTION ))) static _di_pair _DI_UNIQUE_VAR = \
{\
_TO_STRING(CLASS_NAME),\
_TO_STRING(PROTOCOL_NAME),\
};\
#else
__attribute((used, section(_DI_SEGMENT "," _DI_SECTION ))) static _di_pair _DI_UNIQUE_VAR = \
{\
_TO_STRING(CLASS_NAME),\
_TO_STRING(PROTOCOL_NAME),\
};\
#endif

原理很简单:宏供给接口,编译期把类名和协议名写到二进制的指定段里,运转时把这个关系读出来就知道协议是绑定到哪个类了。

  • 下线代码

无用代码删去在一切的功能优化手法里根本上是ROI最低的。可是几乎所 有ROI较高的技术手法都是一次性优化计划,经过几个版别迭代后再做优化就会比较乏力。相比之下,针对代码的检测和删去在很长的一段时刻内供给了很大的优化空间

检测手法:静态扫描Mach-O文件对classlist和classrefs做差集,构成初步的无用类调集,并依据事务代码特征做二次适配

当然还有其他常用的技术手法包含AppCode东西检测以及以例如Pecker这样的基于 IndexStoreDB 、线上核算等。

不过以上计划对Swift的检测计划不太适用(和OC存储差异),这儿能够参阅github.com/wuba/WBBlad…

对项目进行检测,发现还是许多无用类的:

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

然后二次剖析验证,进行优化

3.1.3 二进制重排

iOS体系中虚拟内存到物理内存的映射都是以页为最小单位的。当进程访问一个虚拟内存Page而对应的物理内存却不存在时,就会出现Page Fault缺页中止,(对应System Trace的File Backed Page In) 然后操作体系把数据加载到物理内存中,假如现已现已加载到物理内存了,则会触发Page Cache Hit,后者是比较快的,这也是热发动比冷发动快的原因之一。

虽然缺页中止异常这个处理速度是很快的,可是在一个App的发动进程中或许出现上千(乃至更多)次Page Fault,这个时刻堆集起来会比较显着了。

基于上面原理. 咱们的方针便是在发动的时分增加Page Cache Hit,削减Page Fault,从而到达优化发动时刻的意图
咱们需求确认,在发动的时分,履行了哪些符号,尽或许让这些符号的内存会集在一起,削减占用的页数,就能削减Page Fault的命中次数

程序默许情况下是次序履行的:

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

假如发动需求运用的办法分别在2页Page1和Page2中(method1和method3),为了履行相应的代码,体系就有必要进行两个Page Fault。

假如咱们对办法进行从头排列,让method1和method3在一个Page,那么就能够较少一次Page Fault。

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

经过Instruments中的System Trace东西来看下当时的page fault加载情况

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

这儿有个留意点,为了确保App是真正的冷发动,需求把内存清洁净,不然成果会不太准,下图是我直接杀掉App,从头翻开得到的成果

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

能够看到,和榜首次测验差的有点多,咱们能够在杀掉App后,从头翻开多个其他的App(尽或许多),或许卸载重装,这样在从头翻开App的时分,就会冷发动

综上咱们要做的便是将发动时调用的函数符号会集靠前排列,削减缺页中止数量

  • 获取发动代码履行次序
  • 确认App在发动的时分,调用了哪些函数(运用了哪些符号),这儿引荐一个东西AppOrderFiles(github.com/yulingtianx… ),运用Clang SanitizerCoverage,经过编译器插装的办法,获取到调用函数的符号次序(当然咱们也能够在Build Settings中修正Write Link Map File为YES编译后会生成一个Link Map符号表txt,进行剖析,创立咱们自己的order文件)在App发动后,到首屏VC的viewDidLoad办法内输出order file。

输出的文件在App沙盒,用模拟器运转更方便,得到文件app.order,这儿面便是排好序的符号列表,依据App的履行次序,假如项目比较大的话,会比较久.

把order文件放到工程目录,装备到Xcode里边Build Setting -> Order File -> $(PROJECT_DIR)/xxx.order

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

  • 验证\比照
    Xcode里边Build Setting有个Write Link Map File,能够生成Link Map文件的选项,途径如下

Link Map文件
Intermediates.noindex/xxxx.build/Debug-iphoneos/xxx.build/xxx-LinkMap-normal-arm64.txt
生成app文件途径
Products/Debug-iphoneos/xxx.app

这儿咱们只重视Link Map File的符号表Symbols,这儿的次序便是Mach-O文件对应的次序,假如与xxx.order的次序共同,就标明改成功了

再次经过System Trace东西测验修正前后比照

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

优化前后比照,缺页中止显着削减

获取函数调用符号,采用Clang插桩能够直接hook到Objective-C办法、Swift办法、C函数、Block,能够不用区别对待

3.2 After Main优化

这部分是个大头的优化项,实践场景需求咱们依据自己的详细项目来剖析,但大体遵从一些相同的思路

3.2.1 功用/办法优化

  • 推延&削减I/O操作
  • 此处对项目after main后的发动逻辑剖析不触及IO操作未做优化
  • 控制线程数量
  • 项目中发动阶段线程数量不多且必要,影响不大就未动,但依据各自的项目情况进行剖析治理
  • 发动加载项治理
  • 这儿首要是一些基建和三方/集团SDK初始化使命以及各事务组件工程的发动加载项, 包含前面部分load函数的逻辑放到这儿的发动器来进行调度办理。
  • 咱们能够把这部分做一个发动器进行维护和监控,防劣化。
  • 发动器自注册,注册项包含发动操作闭包,发动履行优先级,发动操作是否后台履行等可选项。
  • 自注册服务无非还是:”发动项:发动闭包 “ 这么一个绑定完结,所以能够相似前面(class-protocol绑定)所讲的思路,将这部分操作写入到可履行文件的DATA段中,运转时再从DATA段取出数据进行相应的操作(调用函数),这样也能够掩盖一切的发动阶段,例如main()之前的阶段。
  • 对项目剖析后,将键盘初始化、地图定位、定见反应还有非主页模块初始化等非必要的发动项下降优先级拖延时机履行。
  • 串行->并行 同步->异步
  • 关于一些耗时操作异步、并行操作,不堵塞主线程的履行
  • 办法耗时核算剖析
  • 核算发动进程事务代码耗时并对耗时办法进行剖析治理
  • 高频次办法调用
  • 有些办法的单个耗时不高,可是频繁调用就会显现耗时,咱们能够加内存缓存,当然了详细场景详细剖析
  • 运用闪屏页的时刻做一些主页UI的预构建
  • 项目中有发动闪屏页,还有榜首次发动弹框隐私页这个空隙做一些首屏操作的前移

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

  • 运用这一段时刻来构建主页UI了、首屏网络数据的预下载、缓存、发动Flutter引擎等作业

3.2.2 首屏烘托优化

屏幕显现遵从一套图形烘托管线来完结终究的显现作业:

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

1.Application阶段(运用内):

Handle Events:

这个进程中会先处理点击事件,这个进程中有或许会需求改动页面的布局和界面层次。

Commit Transaction:

此刻 App 会经过 CPU 处理显现内容的前置核算,比如布局核算、图片解码等使命,之后将核算好的图层进行打包发给 Render Server。(中心Core Animation担任)

Commit Transaction 这部分中首要进行的是:Layout、Display、Prepare、Commit 等四个详细的操作, 最后构成一条事务,经过 CA::Transaction::commit()提交烘托

  • Layout:

构建视图相关,layoutSubviews、addSubview 办法增加子视图、AutoLayout依据 Layout Constraint 核算各个view的frame,文本核算(size)等。

layoutSubviews:在此阶段会调用,可是满意条件如frame,bounds,transform属性改动、增加或许删去view、显式调用setNeedsLayout等

  • Display:

制作视图:交给 Core Graphics 进行视图的制作,得到图元 primitives 数据,留意不是位图数据,位图是GPU阶段依据图元组合而得。可是假如重写了 drawRect: 办法,这个办法会直接调用 Core Graphics 制作办法得到 bitmap 数据,一起体系会额定申请一块内存,用于暂存制作好的 bitmap,导致制作进程从 GPU 搬运到了 CPU,这就导致了必定的功率丢失。与此一起,这个进程会额定运用 CPU 和内存,因而需求高效制作,否则简单形成 CPU 卡顿或许内存爆炸。

  • Prepare:

Core Animation 额定的作业,首要是图片解码和转换,尽量运用GPU支撑的格局, Apple引荐JPG和PNG

比如在UIImageView中展示图片,会经历如下进程: 加载、解码、烘托 简单说便是将一般的二进制数据 (存储在dataBuffer 数据) 转化成 RGB的数据(存储在ImageBuffer), 这个被称为图画的解码decode, 它有如下特色:

decode解码进程是一个耗时进程, 并且是在CPU中完结的. 也便是咱们这部分的prepare中完结。

解码今后的RGB图占用的内存巨细只与bitmap的像素格局(RGB32, RGB23, Gray8 …)和图片宽高有关, 常见bitmap巨细: 每个像素点巨细 width height, 而与原来的紧缩格局PNG, JPG巨细无关.

2.GPU烘托阶段:

首要是一些图元的操作、几许处理、光栅化、像素处理等,不一一细说,这部分操作咱们能做的作业究竟是有限的

所以,咱们大致能够做的优化点如下:

  • 预烘托\异步烘托:
  • 大致思路便是在子线程将一切的视图制作成一张位图,然后回到主线程赋值给 layer的 contents
  • 图片异步解码:
  • 留意这儿并不是将图片加载放到异步线程中在异步线程中生成一个 UIImage或许是 CGImage然后再主线程中设置给 UIImageView,而是在子线程中先将图片制作到CGBitmapContext,然后从bitmap 直接创立图片,常用的图片结构都相似。
  • 按需加载
  • 不需求或许非首屏较为杂乱的视图拖延加载,削减首屏图层的层级
  • 其他:
  • 离屏烘托 尽量削减通明视图个数等等一些细节也要留意

4 成果

经过一些列优化,还是有一些速度的提升,虽然工程还不是大型工程,不过及早继续优化能够避免事务迭代到必定程度难以下手的境地。

iPhone 7p屡次均值

优化前

[函数名:+[LaunchTrace mark]_block_invoke][行号:54]—————App发动————-耗时:pre-main:4.147820
[函数名:+[LaunchTrace mark]_block_invoke][行号:55]—————App发动————-耗时:didfinish:0.654687
[函数名:+[LaunchTrace mark]_block_invoke][行号:56]—————App发动————-耗时:total:4.802507

优化后

[函数名:+[LaunchTrace mark]_block_invoke][行号:54]—————App发动————-耗时:pre-main:3.047820
[函数名:+[LaunchTrace mark]_block_invoke][行号:55]—————App发动————-耗时:didfinish:0.254687
[函数名:+[LaunchTrace mark]_block_invoke][行号:56]—————App发动————-耗时:total:3.302507

pre main阶段下降均匀大约20%, after main阶段均匀下降大约60%, 总体均值下降30%.

当然目前还处于未上线版别,后续上线后凭借监控渠道凭借线上更多数据,更多机型来更好的的进行剖析优化

5 总结

发动速度瓶颈非一日之寒,需求继续的进行优化,这傍边也少不了监控体系的继续建造和优化,日常线上数据的剖析,避免事务快速迭代中的发动速度劣化,对动态库的引入、新增 +load 和静态初始化、发动使命的新增都要参加Code Review机制,优化发动架构为发动这些基础功能保驾护航。

作者:京东物流 彭欣

来历:京东云开发者社区 自猿其说Tech