mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-24 20:00:37 +00:00
docs: message
This commit is contained in:
@@ -27,7 +27,7 @@ _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(p_di
|
||||
|
||||
|
||||
|
||||
#### 1. 屏幕绘制原理
|
||||
### 1. 屏幕绘制原理
|
||||
|
||||

|
||||
|
||||
@@ -70,7 +70,7 @@ _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(p_di
|
||||
|
||||
|
||||
|
||||
#### 2. 卡顿产生的原因
|
||||
### 2. 卡顿产生的原因
|
||||
|
||||
|
||||
|
||||
@@ -90,14 +90,14 @@ CPU 和 GPU 资源消耗原因很多,比如对象的频繁创建、属性调
|
||||
|
||||
|
||||
|
||||
#### 3. APM 如何监控卡顿并上报
|
||||
### 3. APM 如何监控卡顿并上报
|
||||
|
||||
|
||||
CADisplayLink 肯定不用了,这个 FPS 仅作为参考。一般来讲,卡顿的监测有2种方案:**监听 RunLoop 状态回调、子线程 ping 主线程**
|
||||
|
||||
|
||||
|
||||
##### 3.1 RunLoop 状态监听的方式
|
||||
#### 3.1 RunLoop 状态监听的方式
|
||||
|
||||
RunLoop 负责监听输入源进行调度处理。比如网络、输入设备、周期性或者延迟事件、异步回调等。RunLoop 会接收2种类型的输入源:一种是来自另一个线程或者来自不同应用的异步消息(source0事件)、另一种是来自预定或者重复间隔的事件。
|
||||
|
||||
@@ -363,7 +363,7 @@ dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
|
||||
|
||||
|
||||
##### 3.2 子线程 ping 主线程监听的方式
|
||||
#### 3.2 子线程 ping 主线程监听的方式
|
||||
|
||||
开启一个子线程,创建一个初始值为0的信号量、一个初始值为 YES 的布尔值类型标志位。将设置标志位为 NO 的任务派发到主线程中去,子线程休眠阈值时间,时间到后判断标志位是否被主线程成功(值为 NO),如果没成功则认为猪线程发生了卡顿情况,此时 dump 堆栈信息并结合数据上报机制,按照一定策略上传数据到服务器。数据上报会在 [打造功能强大、灵活可配置的数据上报组件](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.80.md) 讲
|
||||
|
||||
@@ -394,7 +394,7 @@ while (self.isCancelled == NO) {
|
||||
|
||||
|
||||
|
||||
#### 4. 堆栈 dump
|
||||
### 4. 堆栈 dump
|
||||
|
||||
方法堆栈的获取是一个麻烦事。理一下思路。`[NSThread callStackSymbols]` 可以获取当前线程的调用栈。但是当监控到卡顿发生,需要拿到主线程的堆栈信息就无能为力了。从任何线程回到主线程这条路走不通。先做个知识回顾。
|
||||
|
||||
@@ -414,7 +414,7 @@ while (self.isCancelled == NO) {
|
||||
|
||||
|
||||
|
||||
#### 5. Mach Task 知识
|
||||
### 5. Mach Task 知识
|
||||
|
||||
**Mach task:**
|
||||
|
||||
@@ -501,7 +501,7 @@ static mach_port_t main_thread_id;
|
||||
|
||||
|
||||
|
||||
#### 1. App 启动时间的监控
|
||||
### 1. App 启动时间的监控
|
||||
|
||||
应用启动时间是影响用户体验的重要因素之一,所以我们需要量化去衡量一个 App 的启动速度到底有多快。启动分为冷启动和热启动。
|
||||
|
||||
@@ -531,7 +531,7 @@ double timeSpan = (timelapse * g_cmmStartupMonitorTimebaseInfoData.numer) / (g_c
|
||||
|
||||
|
||||
|
||||
#### 2. 线上监控启动时间就好,但是在开发阶段需要对启动时间做优化。
|
||||
### 2. 线上监控启动时间就好,但是在开发阶段需要对启动时间做优化。
|
||||
|
||||
要优化启动时间,就先得知道在启动阶段到底做了什么事情,针对现状作出方案。
|
||||
|
||||
@@ -549,7 +549,7 @@ Main 阶段
|
||||

|
||||
|
||||
|
||||
##### 2.1 加载 Dylib
|
||||
#### 2.1 加载 Dylib
|
||||
|
||||
每个动态库的加载,dyld 需要
|
||||
- 分析所依赖的动态库
|
||||
@@ -566,7 +566,7 @@ Main 阶段
|
||||
|
||||
|
||||
|
||||
##### 2.2 Rebase && Binding
|
||||
#### 2.2 Rebase && Binding
|
||||
|
||||
优化:
|
||||
- 减少 Objc 类数量,减少 selector 数量,把未使用的类和函数都可以删掉
|
||||
@@ -575,14 +575,14 @@ Main 阶段
|
||||
|
||||
|
||||
|
||||
##### 2.3 Initializers
|
||||
#### 2.3 Initializers
|
||||
|
||||
优化:
|
||||
- 使用 `+initialize` 代替 `+load`
|
||||
- 不要使用过 attribute*((constructor)) 将方法显示标记为初始化器,而是让初始化方法调用时才执行。比如使用 dispatch_one、pthread_once() 或 std::once()。也就是第一次使用时才初始化,推迟了一部分工作耗时也尽量不要使用 c++ 的静态对象
|
||||
|
||||
|
||||
##### 2.4 pre-main 阶段影响因素
|
||||
#### 2.4 pre-main 阶段影响因素
|
||||
|
||||
- 动态库加载越多,启动越慢。
|
||||
- ObjC 类越多,函数越多,启动越慢。
|
||||
@@ -607,7 +607,7 @@ Main 阶段
|
||||
图片小了,IO操作量就小了,启动当然就会快了,比较靠谱的压缩算法是 TinyPNG。
|
||||
|
||||
|
||||
##### 2.5 main 阶段优化
|
||||
#### 2.5 main 阶段优化
|
||||
|
||||
- 减少启动初始化的流程。能懒加载就懒加载,能放后台初始化就放后台初始化,能延迟初始化的就延迟初始化,不要卡主线程的启动时间,已经下线的业务代码直接删除
|
||||
- 优化代码逻辑。去除一些非必要的逻辑和代码,减小每个流程所消耗的时间
|
||||
@@ -621,7 +621,7 @@ Main 阶段
|
||||
|
||||
|
||||
|
||||
#### 1. CPU 架构
|
||||
### 1. CPU 架构
|
||||
|
||||
CPU(Central Processing Unit)中央处理器,市场上主流的架构有 ARM(arm64)、Intel(x86)、AMD 等。其中 Intel 使用 CISC(Complex Instruction Set Computer),ARM 使用 RISC(Reduced Instruction Set Computer)。区别在于**不同的 CPU 设计理念和方法**。
|
||||
|
||||
@@ -633,7 +633,7 @@ RISC 架构要求软件来指定各个操作步骤。比如上面的乘法,指
|
||||
|
||||
|
||||
|
||||
#### 2. 获取线程信息<a name="threadInfo"></a>
|
||||
### 2. 获取线程信息<a name="threadInfo"></a>
|
||||
|
||||
讲完了区别来讲下如何做 CPU 使用率的监控
|
||||
- 开启定时器,按照设定的周期不断执行下面的逻辑
|
||||
@@ -717,7 +717,7 @@ for (int i = 0; i < threadCount; i++) {
|
||||
|
||||
|
||||
|
||||
#### 1. 基础知识准备
|
||||
### 1. 基础知识准备
|
||||
|
||||
硬盘:也叫做磁盘,用于存储数据。你存储的歌曲、图片、视频都是在硬盘里。
|
||||
|
||||
@@ -729,7 +729,7 @@ iOS 不支持交换空间?不只是 iOS 不支持交换空间,大多数手
|
||||
|
||||
|
||||
|
||||
#### 2. iOS 内存知识
|
||||
### 2. iOS 内存知识
|
||||
|
||||
内存(RAM)与 CPU 一样都是系统中最稀少的资源,也很容易发生竞争,应用内存与性能直接相关。iOS 没有交换空间作为备选资源,所以内存资源尤为重要。
|
||||
|
||||
@@ -804,9 +804,9 @@ App 运行内存 = pageNumbers * pageSize。因为 Compressed Memory 属于 Dirt
|
||||
|
||||
|
||||
|
||||
#### 3. 获取内存信息
|
||||
### 3. 获取内存信息
|
||||
|
||||
##### 3.1 通过 JetsamEvent 日志计算内存限制值
|
||||
#### 3.1 通过 JetsamEvent 日志计算内存限制值
|
||||
|
||||
当 App 被 Jetsam 机制杀死时,手机会生成系统日志。查看路径:Settings-Privacy-Analytics & Improvements- Analytics Data(设置-隐私- 分析与改进-分析数据),可以看到 `JetsamEvent-2020-03-14-161828.ips` 形式的日志,以 JetsamEvent 开头。这些 JetsamEvent 日志都是 iOS 系统内核强杀掉那些优先级不高(idle、frontmost、suspended)且占用内存超过系统内存限制的 App 留下的。
|
||||
|
||||
@@ -1379,7 +1379,7 @@ memorystatus_jld_eval_period_msecs = 6000; /* 6000 msecs == 6 second window */
|
||||
|
||||
|
||||
|
||||
##### 3.2 开发者们整理所得
|
||||
#### 3.2 开发者们整理所得
|
||||
|
||||
[stackoverflow](https://stackoverflow.com/questions/5887248/ios-app-maximum-memory-budget/15200855#15200855) 上有一份数据,整理了各种设备的 OOM 临界值
|
||||
|
||||
@@ -1420,7 +1420,7 @@ memorystatus_jld_eval_period_msecs = 6000; /* 6000 msecs == 6 second window */
|
||||
|
||||
|
||||
|
||||
##### 3.3 触发当前 App 的 high water mark
|
||||
#### 3.3 触发当前 App 的 high water mark
|
||||
|
||||
我们可以写定时器,不断的申请内存,之后再通过 `phys_footprint` 打印当前占用内存,按道理来说不断申请内存即可触发 Jetsam 机制,强杀 App,那么**最后一次打印的内存占用也就是当前设备的内存上限值**。
|
||||
|
||||
@@ -1453,7 +1453,7 @@ timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selec
|
||||
|
||||
|
||||
|
||||
##### 3.4 适用于 iOS13 系统的获取方式
|
||||
#### 3.4 适用于 iOS13 系统的获取方式
|
||||
|
||||
iOS13 开始 <os/proc.h> 中 `size_t os_proc_available_memory(void); ` 可以查看当前可用内存。
|
||||
|
||||
@@ -1533,7 +1533,7 @@ CGFloat memoryUsed = (CGFloat)(vmInfo.phys_footprint/1024.0/1024.0);
|
||||
|
||||
|
||||
|
||||
##### 3.5 通过 XNU 获取内存限制值
|
||||
#### 3.5 通过 XNU 获取内存限制值
|
||||
|
||||
在 XNU 中,有专门用于获取内存上限值的函数和宏,可以通过 `memorystatus_priority_entry` 这个结构体得到所有进程的优先级和内存限制值。
|
||||
|
||||
@@ -1602,7 +1602,7 @@ for 循环打印出每个进程(也就是 App)的 pid、Priority、User Data
|
||||
|
||||
|
||||
|
||||
#### 4. 如何判定发生了 OOM
|
||||
### 4. 如何判定发生了 OOM
|
||||
|
||||
OOM 导致 crash 前,app 一定会收到低内存警告吗?
|
||||
|
||||
@@ -1655,7 +1655,7 @@ for (NSInteger index = 0; index < 10000000; index++) {
|
||||
|
||||
|
||||
|
||||
#### 5. 内存信息收集
|
||||
### 5. 内存信息收集
|
||||
|
||||
要想精确的定位问题,就需要 dump 所有对象及其内存信息。当内存接近系统内存上限的时候,收集并记录所需信息,结合一定的数据上报机制,上传到服务器,分析并修复。
|
||||
|
||||
@@ -1845,7 +1845,7 @@ ASLR: `slide` 函数虚拟地址加载到进程内存的随机偏移量,每
|
||||
|
||||
|
||||
|
||||
#### 6. 开发阶段针对内存我们能做些什么
|
||||
### 6. 开发阶段针对内存我们能做些什么
|
||||
|
||||
1. 图片缩放
|
||||
|
||||
@@ -1934,7 +1934,7 @@ ASLR: `slide` 函数虚拟地址加载到进程内存的随机偏移量,每
|
||||
|
||||
|
||||
|
||||
###1. App 网络请求过程
|
||||
### 1. App 网络请求过程
|
||||
|
||||

|
||||
|
||||
@@ -2998,11 +2998,7 @@ CFNetwork 使用 CFReadStreamRef 来传递数据,使用回调函数的形式
|
||||
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
@@ -3207,7 +3203,7 @@ HTTP 请求报文结构
|
||||
|
||||
下图是在终端使用 `curl` 查看一个完整的请求和响应数据
|
||||
|
||||

|
||||

|
||||
|
||||
我们都知道在 HTTP 通信中,响应数据会使用 gzip 或其他压缩方式压缩,用 NSURLProtocol 等方案监听,用 NSData 类型去计算分析流量等会造成数据的不精确,因为正常一个 HTTP 响应体的内容是使用 gzip 或其他压缩方式压缩的,所以使用 NSData 会偏大。
|
||||
|
||||
@@ -3831,7 +3827,7 @@ KSCrash 功能齐全,可以捕获如下类型的 Crash
|
||||
|
||||
流程图如下:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
@@ -6500,7 +6496,7 @@ Crash log 统一入库 Kibana 时是没有符号化的,所以需要符号化
|
||||
|
||||
3. 监控的各个能力需要做成可配置,灵活开启关闭。
|
||||
|
||||
4. 监控数据需要做内存到文件的写入处理,需要注意策略。监控数据需要存储数据库,数据库大小、设计规则等。存入数据库后如何上报,上报机制等会在另一篇文章讲:[打造一个通用、可配置的数据上报 SDK](./1.80.m)
|
||||
4. 监控数据需要做内存到文件的写入处理,需要注意策略。监控数据需要存储数据库,数据库大小、设计规则等。存入数据库后如何上报,上报机制等会在另一篇文章讲:[打造一个通用、可配置的数据上报 SDK](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.80.md)
|
||||
|
||||
5. 尽量在技术评审后,将各端的技术实现写进文档中,同步给相关人员。比如 ANR 的实现
|
||||
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
|
||||
|
||||
1. https://mp.weixin.qq.com/s?__biz=MzI4MTY5NTk4Ng==&mid=2247489516&idx=1&sn=97c8b0fd84e5fc5b214539c135919884&source=41#wechat_redirect
|
||||
2.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user