一、内存泄漏的核心定义(一句话概括)
内存泄漏指程序中已不再使用的内存无法被垃圾回收机制(GC)释放,导致内存占用持续增长,最终可能引发应用崩溃或性能严重下降。
二、常见内存泄漏场景
1. 全局变量导致的泄漏
javascript
// 错误示例:未声明变量会意外创建全局变量
function leak() {
leakedVar = '这是个全局变量'; // 应使用let/const
}
// 错误示例:this指向全局对象
window.onload = function() {
this.leakedVar = 'window对象被污染'; // 严格模式下会报错
};
2. 未清除的定时器/回调
javascript
// 错误示例:定时器未清除(即使组件/页面已销毁)
const timer = setInterval(() => {
console.log('定时任务');
}, 1000);
// 正确做法:在组件卸载时清除
function cleanup() {
clearInterval(timer);
}
3. DOM引用未释放
javascript
// 错误示例:DOM元素已移除,但JS仍保留引用
const elements = [];
const button = document.createElement('button');
elements.push(button); // 数组保留引用,导致按钮无法被回收
document.body.removeChild(button); // 移除DOM,但JS中仍有引用
4. 闭包导致的泄漏
javascript
// 错误示例:闭包保留外部函数的作用域
function outer() {
const largeData = new Array(1000).fill('data'); // 大数组
return function inner() {
console.log(largeData.length); // 闭包引用largeData,即使outer执行完毕
};
}
const fn = outer(); // 若fn不被释放,largeData也无法被回收
三、如何排查内存泄漏?(展示工具使用能力)
-
Chrome DevTools内存面板:
- 录制Heap Snapshot(堆快照),对比不同时间点的内存占用;
- 查看"Objects retained by closures"(被闭包保留的对象);
- 使用"Allocation instrumentation on timeline"追踪内存分配。
-
Performance面板:
- 录制内存时间线,观察内存增长趋势(是否持续上升不回落)。
-
Lighthouse:
- 运行性能审计,检测潜在的内存泄漏问题。
四、如何解决内存泄漏?(结合场景说方案)
1. 避免全局变量
- 使用
let
/const
声明变量; - 避免在全局作用域定义大型数据结构。
2. 清除定时器和事件监听器
- 在组件销毁时调用
clearInterval
/clearTimeout
; - 使用
addEventListener
时,确保用removeEventListener
解绑(尤其自定义事件)。
3. 管理DOM引用
- 移除DOM元素前,先删除JS中的引用;
- 避免在大型循环中创建大量DOM节点。
4. 优化闭包
- 避免在闭包中引用不必要的大型对象;
- 及时释放不再使用的闭包(如
fn = null
)。
五、实战经验分享(加分项)
"在项目中,遇到过移动端页面卡顿问题,通过Chrome DevTools发现:
- 某个组件频繁创建未释放的定时器,导致内存持续增长;
- 图片懒加载时,已加载的图片DOM被保留在数组中,但页面滚动后未清理。
解决方案:
- 在组件卸载钩子中强制清除所有定时器;
- 使用WeakMap存储DOM引用,允许GC自动回收不再使用的对象。
优化后,页面内存占用从持续增长变为稳定在合理范围,FPS提升了30%。"
六、总结
"内存泄漏本质是GC无法回收不再使用的内存,常见于全局变量、未清除的异步操作、DOM残留引用和闭包。
排查时可用Chrome DevTools的内存和性能面板,解决时需针对性清理资源。在实际项目中,我会在组件生命周期的'销毁'阶段重点检查资源释放,避免内存泄漏累积。"