Skip to content

性能与稳定性

性能优化的原则:先建立度量,再谈优化;没有基线的一切「我觉得卡/快了」都是噪声。稳定性同理:没有崩溃采集,就没有「稳定」可言。本页按「启动 / 流畅 / 内存 / 包体积 / 稳定性」五条主线展开,最后给质量门禁。

建立度量体系

先回答四个问题,再动手:

  1. 启动时间:冷启动(杀进程后点开)到可交互(TTI)多少 ms?按设备档位分位统计(P50/P90);
  2. 流畅度:帧率、掉帧率(卡顿时长占比)、ANR/卡死率;
  3. 内存:RSS 峰值、OOM 率、内存警告次数;
  4. 稳定性:崩溃率(按版本)、崩溃 TOP 榜、崩溃到归因的时长。

工具:各平台官方性能采集 + 三方监控(Sentry / Bugly / Firebase Crashlytics / 自建);关键指标走「发布前后对比」而非单点看。

冷启动优化

冷启动时间线(iOS 为例):进程创建(dyld 加载动态库)→ main → UI 首帧 → 可交互。任何「启动时同步做」的东西都在这条线上。

手法说明
减少启动同步初始化Application/AppDelegate 里能后移的后移:SDK、日志、埋点都异步或懒初始化
避免启动即开大库需要时再加载(DB、图片解码、加密库)
首帧优先首页先用骨架屏/本地缓存渲染,再异步填充
动态库/模块瘦身iOS 主二进制之外的动态库逐个评估能否内联/按需
启动任务清单化分「首帧前必须」与「首帧后可」两组,后组并行且可丢
数据预取窗口首屏后空闲期(idle)再预拉下一页/图片

启动监控要能回答:「哪个函数占了大头」(instrument 启动跟踪 / 自定义打点),而不是凭感觉删初始化

流畅度与帧预算

  • 16ms 帧预算:一帧内主线程 + 渲染不能超过预算,超了就是掉帧。卡顿是「主线程被占」或「渲染超时」两类;
  • 主线程只做 UI:布局、网络解析、图片解码、数据库 IO、加密全部离开主线程;
  • 列表是重灾区:
    • 懒加载(ListView.builder / FlatList / LazyColumn 已讲,见各平台页);
    • item 复用后别做重活;图片解码做小图先行;
  • 动画/滚动触发的大面积重建:加缓存/边界(RepaintBoundary、shouldComponentUpdate 等价物、memo);
  • 卡顿定位流程:复现 → 抓主线程栈(时间片采样)→ 看是「布局/绘制」还是「逻辑占用」→ 修对应端。

内存:泄漏与 OOM

关注点做法
泄漏强引用循环、静态持有 View/Context、未注销观察者/监听;用 LeakCanary/Instruments 工具定期体检
大图采样解码、缩略图、图片库自带缓存与回收
缓存上限内存缓存设容量与低内存清理(iOS memory warning / Android onTrimMemory)
页面退出清理页面销毁时停动画、释放大资源引用、取消在途请求
OOM 归因OOM 常无崩溃栈,用「内存水位 + 时间线」回溯到具体页面/操作

一个反直觉点:内存与卡顿互相放大——内存吃紧触发系统清理/GC/换页,表现为「莫名卡顿」。内存曲线也是性能指标。

包体积

  • 算账:包体积直接吃下载转化率与更新成功率;先把「各类资源占多少」审计出来(资源/代码/三方库/原生二进制);
  • 削减路径:图片转 WebP/AVIF、去重复与未用资源(iOS asset catalog 合并、Android resource shrinking)、代码按需(模块化后动态特性/按需下载资源)、三方库体检(常有大库只用一个小函数)、iOS 拆架构包注意投递规则变化按当前平台策略评估;
  • 别用「压缩壳」逃避审计:包内实际体积没变,只是拆包策略/安装后解压,优化目标是真实交付内容。

稳定性:崩溃与 ANR

  • 采集三件套:崩溃日志(栈 + 现场)、ANR/卡死采样(Android main thread dump)、内存水位快照;
  • 符号化是前提:iOS dSYM、Android mapping(R8/混淆后必须保留并随版本上传),不然堆栈全是乱码——上传 mapping 的自动化要和发版同一条流水线;
  • 崩溃分级:按「影响面 × 频率」排优先级:高频小崩溃比低频大崩溃更伤口碑;按版本对比崩溃率决定是否回滚(配合release 页灰度机制);
  • 三方崩溃服务对比:Sentry(自托管/云)、Bugly(腾讯,国内渠道友好)、Firebase Crashlytics(Google 生态)——按数据合规(国内数据本地化)选型,别只看功能;
  • 稳定性 SLO:给崩溃率定「红线」(如新版本上线 24h 崩溃率超 X 自动暂停放量),发布节奏与监控联动。

日志与问题回溯

用户反馈「刚才闪退/卡死」时,靠三样东西回溯:

  1. 路径日志:关键操作打点(进入页面、发起请求、收到响应、异常分支),崩溃/ANR 现场附最近 N 条;
  2. 版本与环境上下文:App 版本、系统版本、设备型号、网络类型、账号维度——按版本聚合才能快速圈定;
  3. 会话回放(可选):重交互产品可加行为序列,注意隐私合规与脱敏。

日志纪律:调试日志不上生产;生产日志分级(error 必传、info 采样);token/手机号等一律脱敏(呼应网络层与存储)。

质量门禁与常态节奏

  • 发布门槛(示例):冷启动 P90 不超过目标、新版本崩溃率对比前版不恶化、无新增 TOP 崩溃、内存水位回归通过;
  • 常态化:每个版本跑一次「性能回归清单」(启动、列表滚动、图片流、弱网),结果进周报;
  • 技术债:把「性能不达标但能发」设为需要显式决策的事项,而不是默认放行。

常见坑速查

现象对策
无基线就优化改来改去「感觉好了」先建指标,P50/P90 分设备
启动初始化全家桶冷启动随 SDK 增长越来越慢任务分级:首帧前/后,懒初始化
主线程做解码/IO偶发卡顿难复现时间片采样主线程,抓现行
泄漏不体检内存曲线逐日走高LeakCanary/Instruments 每版本体检
图片原图直载OOM采样/缩略/缓存上限
mapping/dSYM 丢失崩溃栈全乱码符号文件与发版同流水线归档
只看崩溃率均值小流量版本数据失真分版本/分端对比 + 样本量校验
生产日志裸奔token/隐私进日志分级 + 脱敏层
低内存不响应被系统杀、体验中断内存警告回调里主动释放
回滚没有开关只能靠发版feature flag 先行,发版兜底
性能回归没人管每版悄悄慢一点发版前性能回归清单成文

检查清单

  • [ ] 启动 / 流畅 / 内存 / 崩溃指标已采并有基线;
  • [ ] 冷启动已按「首帧前/后」分档优化,启动打点能定位大头函数;
  • [ ] 列表与图片走懒加载 + 缩略 + 缓存上限;
  • [ ] 每版本内存体检与泄漏扫描通过;
  • [ ] 崩溃符号化全自动,崩溃率发布对比 + 自动暂停阈值生效;
  • [ ] 日志分级、脱敏、现场上下文齐全;
  • [ ] 性能回归清单进入发版流程。

相关链接:网络层与本地存储 · 打包、签名与发布 · 桌面应用 · 前端性能(Web 侧对照) · 写作规范见客户端开发索引

基于 VitePress 构建 · 内容以知识共享方式沉淀