浏览器可以帮助我们定位什么问题之页面卡顿 & 内存泄漏

133 阅读6分钟

浏览器可以帮助我们定位什么问题

引言

前端性能问题直接影响用户体验和业务指标, 性能优化也是前端主要的课题之一。当遇到页面卡顿、白屏、CPU飙升等核心场景,浏览器可以帮助我们定位什么问题。

1. 问题分类与对应工具矩阵

问题现象核心排查工具辅助工具关键指标
页面卡顿Performance 火焰图Console、SourcesFPS、Long Tasks、主线程阻塞
白屏/加载慢Network + PerformanceCoverage、Lighthouse资源加载时间、首屏渲染时间、 接口调用时间
CPU飙升Performance(火焰图)Task ManagerCPU使用率、函数执行耗时
内存泄漏Memory(内存快照)PerformanceJS堆大小、节点计数、GC频率

2. 关键工具使用解析

2.1 Performance 面板辅助问题定位

Chrome 开发者工具的Performance面板中的火焰图(Flame Chart)是分析网页运行时性能的核心工具,通过可视化主线程的活动帮助开发者定位性能瓶颈。

2.1.1 火焰图基础概念与原理
  1. 视觉结构

    • 横轴:时间线,表示任务执行的持续时间。
    • 纵轴:调用栈深度,顶层是触发的函数,下层是其调用的子函数,形成“倒火焰”形状。
    • 颜色编码:不同事件类型有固定颜色(如黄色= JavaScript 执行,紫色=渲染,绿色=绘制)。
  2. 生成机制
    浏览器以微秒级间隔(100μs~1000μs)对主线程的调用栈采样,连续快照的时间总和即为函数执行时间。采样数据被整合为堆叠的矩形块,宽度代表总耗时。 image.png

2.1.2 Chrome Performance实战指南
步骤1:录制性能数据
  • 运行时性能:点击● Record按钮,操作页面后手动停止。
  • 加载性能:点击🔄“重新加载并记录”按钮,自动捕获页面加载过程。
  • 关键设置
    • CPU节流:模拟移动设备或者其它性能较差设备(如4x减速, 用于复现用户机器问题)。
    • 网络节流:模拟弱网环境(如Slow 4G)。
    • 启用Screenshots:查看每一帧的渲染快照。

image.png

image.png

步骤2:操作区与报告区功能
  • 概览面板:查看FPS(红条=卡顿)、CPU使用率(颜色区分事件类型)、网络请求瀑布流。
  • Main火焰图:主线程活动详情,长任务(>50ms)标红并带⚠️图标。
  • 关键性能指标
    指标说明重要性
    FP/FCP首次绘制/首次内容绘制(解决白屏问题)用户体验第一反馈
    LCP最大内容绘制时间(<2.5秒为优)Core Web Vitals
    DCLDOM解析完成时间可交互准备
2.1.2 火焰图深度解读技巧
  1. 识别长任务(Long Tasks)
    • 任务块右上角有红色三角标记,超过50ms部分用红色实线填充。这类任务会阻塞主线程,导致页面卡顿。

image.png hover 在标红处,查看主要耗时任务行为。

  1. 调用栈分析

image.png

如上图所示

  1. 找出对应火焰图标红的部分
  2. 进行调用栈分析, 找出主要耗时函数, 进行函数优化

image.png 3. 基于火焰图, 即可直接定位到函数代码调用

image.png

image.png

2.2 Memory面板内存分析

Chrome开发者工具中的Memory面板(也称内存面板)是分析和优化网页内存问题的核心工具,用于诊断内存泄漏、内存膨胀及高频垃圾回收等问题。启用 JavaScript Memory 列,监控实时内存变化。若 JavaScript Memory(实时值)持续上升,提示潜在泄漏。

2.2.1 核心功能解析

Memory 面板提供四种分析模式,从不同角度诊断内存问题:

  1. 堆快照(Heap Snapshot)

    • 功能:捕获当前JS堆的完整状态,显示所有对象的内存分配及引用关系。
    • 用途:识别分离的DOM树(内存泄漏主因)、分析对象保留大小(Retained Size)。
    • 视图模式
      • Summary:按构造函数分组对象(如 ObjectArray)。
      • Comparison:对比两次快照的差异,定位新增/未释放对象。 image.png
      • Containment:展示对象层级结构(如 DOMWindow 为全局根节点)。 image.png
      • Statistics:内存占用饼图统计。
  2. 时间轴上的分配插桩(Allocation instrumentation on timeline)

    • 功能:实时记录内存分配事件,通过时间轴可视化分配过程。
    • 用途:定位JS堆内存泄漏的触发点。蓝色竖线表示仍存活的对象,灰色表示已被回收。
    • 高级选项:勾选 Record allocation stacks 可追踪分配时的调用栈。
  3. 分配采样(Allocation sampling)

    • 功能:以低开销采样方式记录内存分配,按函数调用栈细分结果。
    • 用途:识别高内存分配的函数(如高频创建的闭包或大对象)。
    • 视图:默认按“自下而上”排序,顶部显示内存分配最多的函数。
  4. 已分离元素(Detached elements)

    • 功能:专用于检测被JS引用但脱离DOM树的节点(常见内存泄漏源)。
    • 操作:直接记录分离的DOM节点及其引用链。

笔者之前主要是用到的是堆快照

2.2.2 使用场景与问题定位
1. 内存泄漏(渐进式内存增长)
  • 特征:JS堆或DOM节点数随时间持续上升。
  • 排查步骤
    1. 堆快照搜索 Detached 关键字,定位分离的DOM节点。
    2. 通过 Comparison视图 对比操作前后的快照,观察 #New(新增对象)和 #Delta(增量)。
    3. 使用 分配时间轴 记录操作过程,分析蓝色条对应的未回收对象。

image.png

2. 内存膨胀(持续高内存占用)
  • 特征:页面初始加载即占用过高内存(>100MB)。
  • 排查步骤
    • 堆快照 分析 Retained Size 最大的对象(如大数组、缓存未清理)。
    • 通过 分配采样 定位分配大量内存的函数。
3. 频繁垃圾回收(页面卡顿)
  • 特征:JS堆大小频繁波动(任务管理器中内存值快速升降)。
  • 排查步骤
    • Performance面板 启用内存记录,观察JS堆锯齿状波动。
    • 分配时间轴 定位短时间高频分配内存的代码段。

一些值得关注的指标

  • Shallow Size:对象自身占用的内存(不包括引用的对象)。
  • Retained Size:对象被回收后可释放的总内存(含依赖对象)。
  • Distance对象到根节点(如 DOMWindow)的最短引用距离,值越大越可能是泄漏候选。

2.2.3 参考案例以及定位方法

内存泄漏常见模式及修复

  • 分离的DOM节点:移除节点后未置空JS引用(例:detachedTree = null)。
  • 未清除的定时器/事件监听在组件销毁或者页面退出时调用 clearInterval()removeEventListener()
  • 闭包意外持有大对象:避免在闭包内缓存动态增长的数据。
案例1:分离DOM树泄漏
let detachedNode;
document.getElementById('create').addEventListener('click', () => {
  const ul = document.createElement('ul');
  for (let i = 0; i < 1000; i++) {
    ul.appendChild(document.createElement('li'));
  }
  detachedNode = ul; // 节点脱离DOM但被JS引用
});
  • 诊断:堆快照中搜索 Detached,找到 ul 节点 → 查看引用路径为 detachedNode
  • 修复:添加 release 按钮执行 detachedNode = null
案例2:闭包导致的内存增长
const init = () => {
  const data = new Array(1e6).fill('data'); // 大数组
  document.getElementById('btn').addEventListener('click', () => {
    console.log(data.length); // 闭包持有data引用
  });
}
init();
  • 诊断:分配采样显示 init 函数分配大量内存 → 因事件监听未销毁,data 无法释放。
  • 修复:移除事件监听或拆分闭包。