html页面内存泄漏是软件问题而非硬件故障;因事件监听器未解绑、闭包持有dom、全局变量未清理、定时器未清除等导致对象无法被gc回收,需在组件卸载时手动清理。

HTML 页面本身不占内存,真正吃内存的是 DOM 节点、JS 引用、事件监听器、Canvas 上下文、未释放的 iframe 和第三方库实例——只要这些没被正确清理,哪怕页面看着“空”,performance.memory.usedJSHeapSize 也会持续上涨。
DOM 节点过多但又不能删,怎么压内存
10 万个 <div> 每个绑一个 <code>addEventListener,内存轻松破 80 MB。这不是“页面太大”的问题,是“全量挂载+强引用”导致的。
- 改用虚拟滚动:只渲染视口内 + 缓冲区的节点,其余用
height占位撑开滚动条;别自己手写,优先用react-window或vue-virtual-scroller - 避免给每个列表项单独绑定事件,改用事件委托:
listEl.addEventListener('click', e => { if (e.target.matches('.item')) { ... } }) - 批量插入时禁用
innerHTML +=,它会反复重建子树;改用document.createDocumentFragment()构建后一次性append - 清空容器不是
el.innerHTML = ''就完事——得确保没有变量(如oldItems = el.children)还持有对旧节点的引用,否则它们变成Detached DOM tree,GC 不收
JS 引用没断,内存就一直卡着不放
闭包捕获大对象、全局缓存 DOM、监听器没配对移除,是最常见的“内存钉子”。Chrome 堆快照里搜 Detached DOM tree,90% 都指向这些地方。
- 别把 DOM 节点赋给
window.cacheNode或类实例属性;要用缓存,选WeakMap或WeakRef(ES2021+) -
addEventListener必须配对removeEventListener;匿名函数无法移除,别这么写:el.addEventListener('click', () => {...}) - 组件卸载/页面切换时,显式清理:
clearTimeout/clearInterval、AbortController.abort()、ctx.clearRect()、pc.close() - 避免在闭包中传整个
response.data,只取需要的字段;ECharts 的label.formatter这类回调尤其容易捕获整个组件作用域
Canvas/WebGL 和 video 标签偷偷吃内存
getContext('2d') 返回的对象会强引用 canvas 像素数据,哪怕你把 canvas 从 DOM 移除了、变量设为 null,只要上下文还在闭包里,内存就卡死。
- 不再使用时,先清空像素:
context.clearRect(0, 0, canvas.width, canvas.height),再置context = null,最后缩 canvas 尺寸:canvas.width = 1; canvas.height = 1 - 复用
<video></video>元素,别每次播放都document.createElement('video');停止时调mediaSource.endOfStream()→URL.revokeObjectURL(video.src)→mediaSource.close() - WebRTC 场景下,
pc.close()后必须手动置pc = null,并检查有没有未取消的setInterval在轮询getStats() - 确认
chrome://gpu显示 “Hardware-accelerated video decode” 已启用,否则软解会额外吃 CPU 和内存
怎么快速定位是不是真泄漏,而不是“页面大所以内存高”
看任务管理器里的“内存”数字毫无意义。真实泄漏的标志是:performance.memory.usedJSHeapSize 随操作稳定上涨,且 GC 后不回落。
- 打开 Chrome DevTools → Memory 面板 → 点
Take heap snapshot - 操作前拍一次,操作(比如打开弹窗→关闭)后再拍一次,用
Comparison视图看新增的Distance大的对象 - 重点筛:
Detached DOM tree(未被 GC 的孤立节点)、HTMLImageElement(图片解码后未释放)、重复出现的构造函数(如Chart、Editor实例) - 注意:
console.log(obj)会隐式保持对obj的引用,影响快照结果;调试时改用console.table(obj)或打印简单字段
最常被忽略的一点:内存问题往往不出现在“加载时”,而出现在“离开后”——组件卸载了,但定时器、监听器、闭包引用还在。别等用户反馈卡顿才查,每次路由切换或弹窗关闭后,都该主动拍个快照看看 Detached DOM 有没有回落。











