内存排查

3 阅读5分钟

学会用 Chrome DevTools 排查内存泄漏,是前端进阶到架构层面的分水岭。这不仅能解决页面越用越卡的问题,更是大厂资深前端面试的必考实战题。

搞前端内存排查,核心舞台在 Chrome 的 Memory(内存)面板。这里有两把绝世武器:Heap Snapshot(堆快照)Allocation Instrumentation on Timeline(分配时间轴)

下面我们直接用真实的场景,手把手带你去网页上“抓鬼”。


第一步:准备一个“蓄意泄漏”的卡顿现场

为了能百分之百抓到内存泄漏,我们在本地代码中(或者脑海中)先模拟一个最经典的“僵尸 DOM 泄漏”场景:

JavaScript

// 每次点击按钮,都往全局数组里塞一个巨大的 DOM 节点
window.zombieGarbage = [];

function createLeak() {
  const bigNode = document.createElement('div');
  bigNode.innerHTML = new Array(100000).join('👻 我是赖在内存不走的僵尸');
  
  window.zombieGarbage.push(bigNode); // 被全局变量引用,垃圾回收不敢碰它
}

document.getElementById('leakBtn').addEventListener('click', createLeak);

现在,我们打开包含这段代码的网页,按下 F12,切换到 Memory 面板。


第二步:武器一 —— Heap Snapshot(堆快照对比法)

堆快照就像是给内存拍 X 光片。单看一张照片你很难看出问题,但如果拍两张照片进行“前后对比” ,什么东西在异常变大一目了然。

1. 拍照大招流程

  1. 进入 Memory 面板,选择第一项 Heap snapshot
  2. 点击底部的 Take snapshot 按钮。此时生成 Snapshot 1(这是页面刚打开时的干净状态)。
  3. 回到网页上,疯狂点击那个会造成卡顿的按钮 10 次、20 次(或者反复切换几个页面标签)。
  4. 回到 Memory 面板,点击左上角的“小圆点”图标,再次拍一张照片,生成 Snapshot 2

2. 肉眼擒凶:三步看懂对比视图

现在你左侧有两张照片了。点击 Snapshot 2,在面板顶部的下拉菜单中,把默认的 Summary(摘要)切换为 Comparison(对比)

此时,Chrome 会把这两张照片中间新增加的内存物体展示出来。我们在表格里看这三个黄金指标:

  • Delta(数量增量) :如果某个东西的 Delta 表现为 +10+20,说明它一直在增加。
  • Shallow Size(自身大小) :这个对象自己占用了多少内存。
  • Retained Size(保留大小)最核心的指标! 代表这个对象以及它引用的所有子对象加起来的总大小。如果它死掉,能释放多少内存。

🔍 寻找“脱离文档的 DOM(Detached DOM)”

在过滤输入框(Class filter)里输入:Detached(或者输入 HTMLDivElement)。

你会惊奇地发现,列表里蹦出了很多黄色的整行,名字叫 Detached HTMLDivElement

  • 大白话翻译Detached 意味着这个 DOM 已经在页面上被删除了(或者根本没挂载到页面上),但它在 JS 内存里依然活着。这就是典型的僵尸 DOM。

3. 查看 Retainers(到底是谁在拽着它不放?)

点击列表中那个巨大的 Detached HTMLDivElement,看面板最下方的 Retainers(持有者/引用链) 窗口:

  • 你会看到一层层往上指的箭头,像剥洋葱一样。
  • 最终,你会看到一个红色的字样:zombieGarbage in window
  • 破案了! 浏览器明确告诉你:正是因为 window 全局变量下的 zombieGarbage 数组死死拽着这个 DOM,所以垃圾回收车不敢动它。

第三步:武器二 —— Allocation Timeline(时间轴动态抓取)

如果堆快照对比让你觉得繁琐,那这个武器就是“实时监控录像”,它能在你操作页面的同时,用蓝色的柱子实时告诉你内存暴涨的瞬间。

1. 录制流程

  1. 在 Memory 面板选择第三项 Allocation instrumentation on timeline
  2. 点击 Start 开始录制。此时顶部会出现一条带时间轴的横线。
  3. 去页面上做卡顿操作(比如点按钮、滚动列表)。

2. 看图说话:致命的“蓝色小柱子”

在录制过程中,你会看到随着你的点击,时间轴上突突突地冒出很多蓝色的垂直柱子

  • 柱子的高度:代表那一瞬间分配的内存体积。

  • 柱子的颜色

    • 如果是灰色:说明那一瞬间分配了内存,但随后被垃圾回收车成功清理掉了(安全)。
    • 如果是蓝色:说明分配了内存,且一直赖在内存里不走
  • 收网:点击停止。用鼠标在时间轴上框选那一排明晃晃的蓝色柱子。下面列表会自动过滤出那一瞬间导致内存暴涨的具体函数名和对象,直接点击就能跳到 Sources 面板对应的行。


🛠️ 实战避坑与高级技巧

去排查生产环境(线上混淆打包后的项目)时,Heap Snapshot 里的变量名全变成了 abc_t,根本看不出是 zombieGarbage。这时候怎么排查?

技巧 A:利用构造函数名(Constructor)

线上虽然代码混淆了,但是自定义的类名(Class Name)或者第三方库的构造函数名通常是保留的。

  • 如果你怀疑是某个弹窗泄漏了,在过滤框里搜你封装的组件类名(如 DialogEditor)。
  • 如果搜出来一堆已经关闭的弹窗实例,那引用链里顺藤摸瓜总能找到对应的事件监听器(EventListener)。

技巧 B:强制触发垃圾回收(Collect Garbage)

有时候你拿不准某个蓝柱子是正常的临时缓存,还是真正的泄漏。

在 Memory 面板的左上角,有一个“垃圾桶”形状的图标。在拍照或观察时间轴之前,手动点它 3-4 次。这是强行命令浏览器把所有能扫的垃圾全部扫掉。如果点完垃圾桶后,那根蓝柱子依然坚挺存在,那不用怀疑,百分之百是内存泄漏了。