随着快手 App 功能越来越多,开发同学不断增加,性能方面面临的挑战也愈发严峻,诸如 App 越来越卡顿、内存占用越来越大、包大小不断增加等各类问题都严重影响着用户体验。 为了保证体验,我们针对这几类问题分别进行了深入优化,并构建了一套比较完整的移动客 户端性能监控平台,本次分享将重点介绍我们在实践过程中的干货技术内容。
演讲提纲:
1. 快手 APM 系统(指标监控)
主要解决我们面临的各种稳定性、性能问题。
- 优化了 40%的启动速度,提升了我们的 0 播、留存等关键产品数据。
- 优化了 23M 包大小,大大降低了我们新用户获取成本。
- 两个优化,都获得了快手技术线的绩效提升奖
2. 启动优化
1)定位问题
- 将启动时运行的代码,按照功能,做成 task
- 线上收集每个 task 的耗时 ○ 线下 android 利用 systrace、iOS 自研火焰图工具,分析耗时
2) 一些优化手段
- 优化整体流程
- 分场景、分用户特性,推迟甚至取消一些 task (用户登录状态,用户使用习惯,后面 我们会用机器学习做相关优化)
- 特别关注一些锁的等待、主线程 cpu 分配不足的问题
- 一些系统 api,背后会引发一系列的初始化(setcookie,会引起 webview 内核初始 化)
- 主动 dex2oat
- 二进制、dex 重排
3) 监控、维持
- 线下我们主要通过搭建实验室环境,每个小时跑定时任务,对比当前代码和一小时前 启动耗时的变化,如果超过阈值,我们就触发二分查找问题,知道找到引起恶化的 mr,报警并指给代码提交人。
- 线上埋点、分版本、机型、系统多纬度监控
3. 内存优化
1) java:
- 内存镜像转储
- 研发了一种高效 dump 方案,解决了传统方法虚拟机内存转储需要暂停虚拟机的问题
- 内存镜像分析
- 研发了基于 shark 开发了低内存开销、低 cpu 开销的独立进程解析方案
- 采用了更为节省内存的高性能数据结构,采用了更为高效内存索引,增加了同类 型对象阈值用于 gc root 最短路径搜索剪枝
- 效果:可以在手机侧 10 分钟内完成 400m 镜像、200w 对象的极端 case 解析
- 内存镜像裁剪
- 研发了一种 hook 虚拟机内存镜像转储时 IO 的高效裁剪方案,解决了传统裁剪效 率低、成功率低的问题
- 辅以 zstd 压缩,90%内存镜像可以压缩至 80m 内
2) native:
- 利用编译器插桩及 malloc hook 记录所有活着的内存块「包含内存块地址、 backtrace 信息」,对性能影响较小
- 利用 mark-and-sweep 算法在单独的进程中分析测试应用进程 Native Heap 中不 可达的内存块「包含内存块地址」
- 对于步骤 2 中收集到的不可达内存块,从 1 中获取其对应的 backtrace 信息,将 泄漏信息上报至 APM 监控平台
- APM 监控平台解析泄漏信息「backtrace 信息符号化等」,做友好的展示,业务 方根据 APM 展示信息可快速定位泄漏问题
4. 卡顿优化
1) UI 卡顿
- 监控:
- 每隔 100ms dump 一下当前主线程的调用栈
- 监控 main looper 的消息处理,如果处理时长超过 1s ,我们就认为发生了卡顿 快手内部文档请勿外传
- 将前 1s 记录的所有调用栈做对比,找到相邻并且一致的,那么就说明这个调用栈 持续了 100ms 以上(如果 10 次都相同,那么这个调用栈持续了 1s)
- 将所有持续时间最长的调用栈上报(这里我们认为它是引起这次卡顿的主要原 因),后端根据堆栈做聚合、展示
- 优化
- x2c
- 异步 inflate
- mmap 替换 sp
- 优化多线程同步
- 通过 hack 办法绕开系统 api 卡顿
- 解决 selector 中同一个 bitmap 会 decode 两次的问题
- 视频卡顿 ○ 关注百秒卡顿、起播时长
- dns 预解析
- 动态码率
- buffer size 的取值考虑(首帧时长和卡顿率在这个指标上是矛盾的)
- 预缓存
- 降低动态码率波动范围
听众收益:
深入了解过亿用户、使用时间超长的产品,怎样搭建 APM,保证 app 质量
适合人群:
android iOS 开发人员,对于移动端性能比较关注的人员