← 返回题目列表

前端内存泄漏如何排查?常见泄漏场景有哪些?

中等 第 19 / 29 题 更新于 2026/07/29
前端性能内存泄漏DevTools性能排查

简化版

前端内存泄漏是指不再需要的对象仍被引用,无法被 GC 回收,导致页面越用越慢甚至崩溃。常见原因包括未清理定时器、事件监听、全局缓存、闭包引用、大 DOM 被 JS 持有、订阅未取消、组件卸载后异步回调仍更新状态。排查通常用 Chrome DevTools 的 Memory 面板做 Heap Snapshot、Allocation Timeline,对比操作前后对象是否持续增长。

详细版

排查内存泄漏的关键是判断“对象为什么还活着”。浏览器 GC 会从根对象出发标记可达对象,只要还有引用链,对象就不会释放。

场景泄漏原因修复方式
定时器回调闭包持有组件数据卸载时 clear
事件监听DOM 或 window listener 未移除removeEventListener
全局缓存Map 长期保存对象设置上限或 WeakMap
异步请求回调引用已卸载组件AbortController 或状态判断
useEffect(() => {
  const onResize = () => {}
  window.addEventListener('resize', onResize)
  return () => window.removeEventListener('resize', onResize)
}, [])

内存排查不要只看“用了多少 MB”,更要看重复操作后内存是否持续回不到基线。

完整版教学

一、什么是前端内存泄漏

内存泄漏不是内存用得多,而是不该继续存在的对象仍然被引用。比如用户关闭弹窗后,弹窗 DOM、事件回调和数据还被全局数组引用,它们就无法被回收。

大型单页应用尤其容易遇到这个问题,因为页面不刷新,组件反复进入和离开,泄漏会慢慢累积。

二、GC 的核心判断

JavaScript 引擎会从根对象出发查找可达对象。常见根对象包括 window、当前调用栈、闭包、DOM 引用、定时器队列和事件监听。

只要一个对象还能通过引用链访问到,GC 就不会回收它。因此排查泄漏时要找 retaining path,也就是保留它的引用路径。

三、常见泄漏场景

常见问题非常朴素:

  • setInterval 没有清理。
  • windowdocument 上的事件监听没移除。
  • WebSocket、事件总线、RxJS 订阅没取消。
  • 全局数组、Map、缓存无限增长。
  • 闭包保存大对象。
  • DOM 删除后仍被 JS 变量引用。
  • 组件卸载后异步回调仍保存状态。

四、如何用 DevTools 排查

Chrome DevTools 的 Memory 面板常用三种方式:

工具用法适合问题
Heap Snapshot拍快照并比较找对象引用链
Allocation instrumentation录制分配过程找操作中分配的对象
Performance monitor观察 JS heap 趋势看是否持续增长

排查步骤通常是:打开页面,记录基线;重复进入离开某功能 5 到 10 次;手动触发 GC;拍快照对比;查看增长对象和 retaining path。

五、框架里的清理模式

React、Vue 都提供生命周期清理点。React 用 useEffect 返回清理函数,Vue 用 onUnmounted

import { onUnmounted } from 'vue'

const timer = window.setInterval(() => {
  // polling
}, 1000)

onUnmounted(() => {
  window.clearInterval(timer)
})

凡是“注册了外部资源”的地方,都应该有对应清理。

六、缓存也会制造泄漏

很多泄漏不是忘记清理,而是缓存策略不设边界。比如用 Map 缓存接口响应、图片对象或组件实例,时间一长就无限增长。

可以考虑:

  • LRU 上限。
  • 过期时间。
  • 页面离开时清理。
  • 弱引用场景使用 WeakMap

七、常见误区与追问

  • 误区:内存高就是泄漏。 浏览器可能为了性能暂不回收,关键看重复操作后是否持续增长且回不到基线。
  • 误区:框架会自动清理所有东西。 框架能清理自身组件树,但清不了你注册到 window、事件总线或定时器里的引用。
  • 误区:只要把 DOM 删除就释放了。 如果 JS 变量、闭包或缓存还引用 DOM,它仍然无法回收。
  • 追问:Heap Snapshot 看什么? 看增长对象、构造函数、retained size 和 retaining path。
  • 追问:为什么 WeakMap 有帮助? WeakMap 的 key 不阻止 GC,适合给对象附加元数据。
  • 追问:异步请求怎么避免泄漏? 用 AbortController 取消请求,或在回调中判断组件是否仍然存活。

八、加强记忆

内存泄漏可以记成“对象还被谁拽着”。排查时重复操作、回收、拍快照、看引用链;修复时清理定时器、监听、订阅、缓存和异步任务,让对象离开页面后真正不可达。