举一个基于链接优化启动速率的例子:
最开始讲解 Page In 的时间,我们提到 TEXT 段的页解密很耗时,有没有办法优化呢?
可以通过 ld 的-rename_p,把 TEXT 段中的内容,好比字符串移动到其他的段(启动路径上难免会读很多字符串),从而规避这个解密的耗时。抖音的重命名方案:
“-Wl,-rename_p,__TEXT,__cstring,__RODATA,__cstring”,
“-Wl,-rename_p,__TEXT,__const,__RODATA,__const”,
“-Wl,-rename_p,__TEXT,__gcc_except_tab,__RODATA,__gcc_except_tab”,
“-Wl,-rename_p,__TEXT,__objc_methname,__RODATA,__objc_methname”,
“-Wl,-rename_p,__TEXT,__objc_classname,__RODATA,__objc_classname”,
“-Wl,-rename_p,__TEXT,__objc_methtype,__RODATA,__objc_methtype”
裁剪
编译完 Mach-O 之后会进行裁剪(strip),是由于里面有些信息,如调试符号,是不必要带到线上去的。裁剪有多种级别,一样平常的设置如下:
- All Symbols,主二进制
- Non-Global Symbols,动态库
- Debugging Symbols,二方静态库
为什么二方库在出静态库的时间要选择 Debugging Symbols 呢?是由于像 order_file 等链接期间的优化是基于符号的,如果把符号裁剪掉,那么这些优化也就不会见效了。
署名 & 上传
裁剪完二进制后,会和编译好的资源文件一起打包成.app 文件,接着对这个文件进行署名。署名的作用是保证文件内容不多不少,没有被篡改过。接着会把包上传到 iTunes Connect,上传后会对__TEXT段加密,加密会削弱 IPA 的压缩效果,增长包大小,也会降低启动速率**(iOS 13 优化了加密过程,不会对包大小和启动耗时有影响)**。
dyld3 启动流程
Apple 在 iOS 13 上对第三方 App 启用了 dyld3,官方数据[3]表现,已往四年新发布的设备中有 93%的设备是 iOS 13,所以我们重点看下 dyld3 的启动流程。
Before dyld
用户点击图标之后,会发送一个系统调用 execve 到内核,内核创建进程。接着会把主二进制 mmap 进来,读取 load command 中的 LC_LOAD_DYLINKER,找到 dyld 的的路径。然后 mmap dyld 到捏造内存,找到 dyld 的入口函数_dyld_start,把 PC 寄存器设置成_dyld_start,接下来启动流程交给了 dyld。
注意这个过程都是在内核态完成的,这里提到了 PC 寄存器,PC 寄存器存储了下一条指令的地址,程序的实验就是不断修改和读取 PC 寄存器来完成的。
dyld
创建启动闭包
dyld 会首先创建启动闭包,闭包是一个缓存,用来提拔启动速率的。既然是缓存,那么必然不是每次启动都创建的,只有在重启手机或者更新/下载 App 的第一次启动才会创建。闭包存储在沙盒的 tmp/com.apple.dyld 目录,整理缓存的时间切记不要整理这个目录。
闭包是怎么提拔启动速率的呢?我们先来看一下闭包里都有什么内容:
- dependends,依赖动态库列表
- fixup:bind & rebase 的地址
- initializer-order:初始化调用顺序
- optimizeObjc: Objective C 的元数据
- 其他:main entry, uuid…
动态库的依赖是树状的布局,初始化的调用顺序是先调用树的叶子结点,然后一层层向上,最先调用的是 libSystem,由于他是全部依赖的源头。
为什么闭包能进步启动速率呢?
由于这些信息是每次启动都必要的,把信息存储到一个缓存文件就能避免每次都剖析,尤其是 Objective C 的运行时数据(Class/Method**…)剖析非常****慢。**
fixup
有了闭包之后,就可以用闭包启动 App 了。这时间很多动态库还没有加载进来,会首先对这些动态库 mmap 加载到捏造内存里。接着会对每个 Mach-O 做 fixup,包括 Rebase 和 Bind。
- Rebase:修复内部指针。这是由于 Mach-O 在 mmap 到捏造内存的时间,起始地址会有一个随机的偏移量 slide,必要把内部的指针指向加上这个 slide。
- Bind:修复外部指针。这个比较好明白,由于像 printf 等外部函数,只有运行时才知道它的地址是什么,bind 就是把指针指向这个地址。
举个例子:一个 Objective C 字符串@“1234”,编译到最后的二进制的时间是会存储在两个 p 里的
- __TEXT,__cstring,存储实际的字符串"1234"
- __DATA,__cfstring,存储 Objective C 字符串的元数据,每个元数据占用 32Byte,里面有两个指针:内部指针,指向__TEXT,__cstring中字符串的位置;外部指针 isa,指向类对象的,这就是为什么可以对 Objective C 的字符串字面量发消息的缘故原由。
如下图,编译的时间,字符串 1234 在__cstring的 0x10 处,所以 DATA 段的指针指向 0x10。但是 mmap 之后有一个偏移量 slide=0x1000,这时间字符串在运行时的地址就是 0x1010,那么 DATA 段的指针指向就不对了。Rebase 的过程就是把指针从 0x10,加上 slide 变成 0x1010。运行时类对象的地址已经知道了,bind 就是把 isa 指向实际的内存地址。
LibSystem Initializer
Bind & Rebase 之后,首先会实验 LibSystem 的 Initializer,做一些最基本的初始化:
- 初始化 libdispatch
- 初始化 objc runtime,注册 sel,加载 category
注意这里没有初始化 objc 的类方法等信息,是由于启动闭包的缓存数据已经包罗了 optimizeObjc。
Load & Static Initializer
接下来会进行 main 函数之前的一些初始化,主要包括+load 和 static initializer。这两类初始化函数都有个特点:调用顺序不确定,和对应文件的链接顺序有关系。那么就会存在一个隐蔽的坑:有些注册逻辑在+load 里,对应会有一些地方读取这些注册的数据,如果在+load 中读取,很有可能读取的时间还没有注册。
那么,如何找到代码里有哪些 load 和 static initializer 呢?
在 Build Settings 里可以设置 write linkmap,这样在天生的 linkmap 文件里就可以找到有哪些文件里包罗 load 或者 static initializer:
- __mod_init_func,static initializer
- __objc_nlclslist,实现+load 的类
- __objc_nlcatlist,实现+load 的 Category
load 举例
如果+load 方法里的内容很简单,会影响启动时间么?好比这样的一个+load 方法?
+ (void)load { printf(“1234”); }
编译完了之后,这个函数会在二进制中的 TEXT 两个段存在:__text存函数二进制,cstring存储字符串 1234。为了实验函数,首先要访问__text触发一次 Page In 读入物理内存,为了打印字符串,要访问__cstring,还会触发一次 Page In。
- 为了实验这个简单的函数,系统要额外付出两次 Page In 的代价,所以 load 函数多了,page in 会成为启动性能的瓶颈。
static initializer 产生的条件
静态初始化是从哪来的呢?以下几种代码会导致静态初始化
- __attribute__((constructor))
- static class object
- static object in global namespace
注意,并不是全部的 static 变量都会产生静态初始化,编译器很智能,对于在编译期间就能确定的变量是会直接 inline。
//会产生静态初始化
class Demo{
static const std::string var_1;
};
const std::string var_2 = “1234”;
static Logger logger;//不会产生静态初始化
static const int var_3 = 4;
static const char * var_4 = “1234”;
std::string 会集成 static initializer 是由于初始化的时间必须实验构造函数,这时间编译器就不知道怎么做了,只能耽误到运行时~
UIKit Init
+load 和 static initializer 实验完毕之后,dyld 会把启动流程交给 App,开始实验 main 函数。main 函数里要做的最重要的事情就是初始化 UIKit。UIKit 主要会做两个大的初始化:
- 初始化 UIApplication
- 启动主线程的 Runloop
由于主线程的 dispatch_async 是基于 runloop 的,所以在+load 里如果调用了 dispatch_async 会在这个阶段实验。
Runloop
线程在实验完代码就会退出,很显着主线程是不能退出的,那么就必要一种机制:事件来的时间实验任务,否则让线程休眠,Runloop 就是实现这个功能的。
Runloop 本质上是一个While 循环,在图中橙色部分的 mach_msg_trap 就是触发一个系统调用,让线程休眠,等待事件到来,唤醒 Runloop,继续实验这个 while循环。
Runloop 主要处理几种任务:Source0,Source1,Timer,GCD MainQueue,Block。在循环的合适时机,会以 Observer 的方式通知外部实验到了那里。那么,Runloop 与启动又有什么关系呢?
- App 的 LifeCycle 方法是基于 Runloop 的 Source0 的
- 首帧渲染是基于 Runloop Block 的
Runloop 在启动上主要有几点应用:
- 精准统计启动时间
- 找到一个时机,在启动结束去实验一些预热任务
- 使用 Runloop 打散耗时的启动预热任务
自我先容一下,小编13年上海交大结业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提拔技能,往往是本身探索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。本身不成体系的自学效果低效又漫长,而且极易遇到天花板技术停滞不前!
因此网络整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提拔又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零根本资料,也有适合3年以上履历的小伙伴深入学习提拔的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包罗大厂面经、学习条记、源码课本、实战项目、讲解视频,并且后续会持续更新
如果你以为这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
总结
开发是面向对象。我们找工作应该更多是面向面试。哪怕进大厂真的只是去宁螺丝,但你要进去得先学会面试的时间造飞机不是么?
作者13年java转Android开发,在小厂待过,也去过华为,OPPO等,去年四月份进了阿里一直到现在。等大厂待过也面试过很多人。深知大多数初中级Android工程师,想要提拔技能,往往是本身探索成长,不成体系的学习效果低效漫长且无助。
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精神),包罗知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
资料太多,全部展示会影响篇幅,暂时就先枚举这些部分截图
当程序员轻易,当一个良好的程序员是必要不断学习的,从低级程序员到高级程序员,从低级架构师到资深架构师,或者走向管理,从技术司理到技术总监,每个阶段都必要把握不同的能力。早早确定本身的职业方向,才能在工作和能力提拔中甩开同龄人。
本文已被CODING开源项目:《Android学习条记总结+移动架构视频+大厂面试真题+项目实战源码》收录
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感爱好,欢迎戳这里参加程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模子、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产物司理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据布局与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
不同的能力。早早确定本身的职业方向,才能在工作和能力提拔中甩开同龄人。
本文已被CODING开源项目:《Android学习条记总结+移动架构视频+大厂面试真题+项目实战源码》收录
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感爱好,欢迎戳这里参加程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模子、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产物司理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据布局与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |