实战录制:用 Performance 面板追踪卡顿根源

说实话,很多同学做内存泄漏排查,第一反应就是打开 Chrome 的 Memory 面板,等着快照比较。但我觉得,真正卡顿的那几秒里,主线程到底在忙什么,才是破案的关键。今天我们就来聊聊 Performance 面板怎么录、怎么看、怎么抓住那些“长任务”。

1. Record 录制主线程活动

先说我自己的习惯。遇到卡顿,我不是直接点 Memory,而是先打开 Performance,点击那个圆点 Record。录的时候,我会让页面重复几次有问题的操作(比如滚动、点击弹窗、切换 tab),然后停录。

看下录制后的界面:

录制区域看什么
顶部时间轴总时长、事件序列、FPS 下探的区间
主线程火焰图黄色 / 紫色的长条就是任务栈,越宽越耗时
摘要面板选中某段时间,看函数名、耗时、调用关系
长任务标记红色对勾或者红色虚线框,就是 Long Task
小技巧: 录制前先清一下缓存,并且把 CPU 降速到 4x 或 6x。这样卡顿更容易复现,而且录制出来的火焰图更“明显”。我遇到过一个 case,不降速根本看不出来,降速后才发现有个定时器在疯狂创建 DOM 节点。

2. 火焰图——卡顿的显微镜

火焰图长什么样?说白了就是一个堆叠的彩色条形图。每个方块代表一个函数调用,方块宽度代表执行时间。越靠近底部的是根调用,越往上是子函数。

我记得最有用的一条经验:从火焰图的顶部往下看。如果某个宽条一直不变,颜色还特别亮(比如紫色代表渲染),那就可疑。我曾经碰到一个页面,每次滚动都会触发 updateWidget,这个函数调了近 200ms,而它内部竟然在重新遍历一个 5000 项的数组,每次都新建对象。这就是泄漏的前兆——对象越来越多,遍历越来越慢。

解读步骤

  1. 找到最宽的方块——时间消耗大户。
  2. 看调用栈——点它,下面会显示从入口到该函数的完整路径。
  3. 留意重复模式——同一个宽条反复出现,说明可能有人在循环里做重计算。
  4. 检查 GC 事件——火焰图里灰白色的长条可能是垃圾回收,如果它频繁出现且持续时间超过 50ms,说明内存碎片化严重。
重点: 别被“长任务”字样吓到。Chrome 把任何超过 50ms 的任务标为 Long Task,但 40ms 的任务也很危险。你想想看,屏幕刷新率 60Hz,一帧只有 16.6ms,一个任务占了 40ms 就意味着这个帧必掉。

3. 长任务识别——抓出“罪魁祸首”

在 Performance 录制结果里,长任务通常会用红色标记(一个红色的对勾或者红色半透明方块)。你可以直接点击那个标记,右侧会显示它的调用栈和持续时间。

我曾经在排查一个复杂表格组件时,发现有个长任务持续了 350ms。点进去一看,堆栈深处有个 scrollHandler,里面调用了 getBoundingClientRect 和 offsetHeight——这两个都是强制回流。而且因为上一次滚动后 DOM 结构变了,样式计算被反复触发。这就是典型的“布局抖动 + 内存泄漏”组合拳。

长任务常见类型

类型典型耗时可能原因
JS 执行> 80ms大循环、字符串拼接、不必要的深拷贝
样式计算> 20ms大量选择器匹配、层叠样式表过长
布局> 30ms频繁读写 offsetTop, scrollLeft
垃圾回收> 30ms大量短期对象、闭包引用未释放
避坑指南: 我以前吃过一个亏:Performance 录了 10 秒,结果火焰图全是灰白色的 GC 块,但我只看红色长任务标记,差点漏掉。其实 GC 本身就是一种任务,如果它频繁并且超 50ms,你要怀疑是不是有内存泄漏导致 GC 耗时越来越长。所以,不要只看红色标记,还要看甘特图里的所有“长”条。

知识结构图:从录制到定位的完整流程

下面的 SVG 图总结了整个排查路线:

Performance定位卡顿流程 ① 点击 Record 开始录制主线程 ② 复现卡顿操作 多次触发问题场景 ③ 停止录制 得到火焰图 + 时间轴 ④ 识别长任务(红色标记) 点击查看调用栈 ⑤ 检查 GC 任务 是否频繁 > 50ms ⑥ 疑似内存泄漏 转 Memory 快照对比 ⑦ 分析函数调用栈 定位耗时函数 & 内存增长 ✅ 定位卡顿根因 提示:GC 时间过长 + 函数重复调用 大概率是内存泄漏 + 强制回流

实战小贴士

  • 录制时长不要太长——我建议每次 5-8 秒,太长难以分析,太短可能捕捉不到卡顿。
  • 配合控制台日志——在可疑的代码位置加 console.time 和 console.timeEnd,这样 Performance 里能看到自定义标记,更容易对应。
  • 导出录制结果——点击右上角的“保存”,导出 .json 文件。发给同事或事后复盘很方便。
一句话总结: Record 录的是时间线,火焰图给的是调用栈,长任务是“报警器”。三者结合,你就能准确知道——哪一段代码、什么时候、花了多少时间、为什么卡。这是内存泄漏排查中最直观的一步。