前端内存泄漏排查指南
更新: 5/4/2026 字数: 0 字 时长: 0 分钟
页面越用越卡、最终崩溃,典型的内存泄漏表现。下面是一套从定位 → 分析 → 修复的完整排查路径。
一、先确认是不是内存问题
打开 Chrome DevTools → Performance Monitor(或 Task Manager: Shift+Esc),观察:
- JS heap size 是否随操作持续增长且不回落
- DOM Nodes 数量是否只增不减
- Listeners 数量是否异常增长
如果三者都随时间单调上升,基本可以确定是内存泄漏。
二、定位泄漏点:Memory 面板三板斧
1. Heap Snapshot(堆快照对比法)——最常用
核心思路:执行可疑操作前后分别打快照,对比新增对象。
操作步骤:
- 进入可疑页面,先点 Collect garbage(垃圾桶图标)强制 GC
- 打第一个 Snapshot(基线)
- 重复执行可疑操作 N 次(如反复打开/关闭弹窗、切换路由)
- 再次 GC,打第二个 Snapshot
- 切换到 Comparison 视图,按
# Delta排序 - 重点看
Detached开头的对象(脱离 DOM 但仍被引用的节点)
2. Allocation instrumentation on timeline(时间轴分配)
录制一段用户操作,查看内存分配的时间分布。蓝色竖条在录制结束后仍存在,就是未被回收的对象,点击可直接看到分配调用栈。
3. Allocation sampling(低开销采样)
适合长时间录制,开销小,能看出哪些函数在疯狂分配内存。
三、前端内存泄漏的五大常见元凶
| 类型 | 典型代码 | 修复方式 |
|---|---|---|
| 未解绑事件监听 | window.addEventListener 组件卸载未 remove | return () => removeEventListener(...) |
| 定时器未清理 | setInterval / setTimeout 闭包持续持有组件引用 | clearInterval on unmount |
| 闭包意外引用 | 回调捕获大对象,生命周期长于对象 | 解除引用 bigObj = null |
| 游离 DOM | 删除 DOM 前已被 JS 变量/数组持有 | 先解引用再 remove |
| 全局缓存无上限 | window.cache[key] = ... 无淘汰策略 | 改用 WeakMap 或 LRU |
框架特有:
- React:
useEffect忘记返回 cleanup;闭包捕获旧 state;Context 传递大对象 - Vue:
$on/ EventBus 未$off;watch未停止 - 第三方库:ECharts / Leaflet / Monaco 未调用
dispose();WebSocket 未close()
四、线上排查(无法开 DevTools 场景)
- performance.memory API(Chrome):采集
usedJSHeapSize上报监控,画出趋势图定位爆发时间点 - 灰度 + 复现路径还原:结合用户操作路径(埋点)在本地复现
- Sentry / 字节内部的 Slardar 等 APM:订阅 OOM / 白屏事件,关联用户会话回放
五、快速自检 Checklist
js
// 常见修复模板 - React
useEffect(() => {
const handler = () => { /* ... */ };
window.addEventListener('resize', handler);
const timer = setInterval(fn, 1000);
const ws = new WebSocket(url);
return () => {
window.removeEventListener('resize', handler);
clearInterval(timer);
ws.close();
};
}, []);
// 大对象缓存
const cache = new WeakMap(); // key 被回收时自动清理六、推荐排查顺序
- Performance Monitor 看三条曲线确认泄漏存在
- Heap Snapshot 对比 → 找到新增最多的 Detached 对象 / 闭包
- 点进 Retainers(保留链)→ 找到谁还在引用它
- 顺着引用链回到源码 → 定位未清理的监听/定时器/缓存
- 修复后重复第 2 步验证曲线回落
需要我针对你页面用的框架(React / Vue)给更具体的代码审查清单,或帮你看某段具体代码是否有泄漏风险吗?