一、垃圾回收核心
1. 两种回收策略
| 策略 | 原理 | 缺点 |
|---|---|---|
| 标记清除 | 从根对象出发标记可达对象,清除未标记的 | 全堆遍历性能消耗 |
| 引用计数 | 统计引用次数,次数为0时回收 | 循环引用无法回收 |
循环引用示例:
function leak() {
let obj1 = {a: obj2}; // ←┐
let obj2 = {a: obj1}; // ─┘
}
2. 避免回收性能问题
- 数组清空用
arr.length = 0而非arr = [] - 不再用的对象设为
null - 复用函数避免重复创建
二、内存泄漏四大场景
| 场景 | 示例代码 | 解决方法 |
|---|---|---|
| 意外全局变量 | function() { leakVar = '未声明变量' } | 使用严格模式 'use strict' |
| 未清除定时器 | setInterval(() => {...}, 1000) 未调用 clearInterval | 组件卸载时清理定时器 |
| 残留DOM引用 | const dom = document.getElementById('del'); dom.parentNode.removeChild(dom) 但保留 dom 变量 | 删除后置 dom = null |
| 闭包不当使用 | function outer() { let bigData = ...; return function() { /* 未用bigData */ }} | 及时解除闭包引用 |
三、高频面试题
-
如何检测内存泄漏?
→ Chrome DevTools的Memory面板:- 拍快照对比内存增长
- 使用Performance录制查看内存曲线
-
闭包一定会导致内存泄漏吗?
→ 不会!只有闭包中保留不再需要的大对象才会泄漏
→ 正确做法:使用后解除引用fn = null -
WeakMap/WeakSet 如何避免内存泄漏?
→ 键是弱引用,不影响垃圾回收const wm = new WeakMap(); let obj = {}; wm.set(obj, 'data'); obj = null; // WeakMap中的条目自动清除
四、记忆口诀
"标记清除找可达,引用计数循环卡
泄漏四兄弟:全局、定时、DOM、闭包
严格模式加清理,弱引用解难题"
回答:
首先浏览器的垃圾回收机制是指js代码在运行时,需要分配内存空间来储存变量和值。当变量不再参与运行时,就需要从系统中收回被占用的内存空间,这就是垃圾回收机制。
常用的回收策略有两个
一个是引用计数策略,统计对象的引用次数,次数为零时进行回收,但此时就会出现一个漏洞就是在堆中循环引用的无法回收,如果让let obj1 ={a:obj2};let obj2={a:obj1};这样循环引用的次数不会为零,就不会去回收。 另一个是标记清除策略,从根对象出发标记可达对象,清粗未标记的。此时堆中循环引用的情况就可以进行垃圾回收了。
再一个问题是内存泄漏
内存泄漏指的是程序中不再需要的内存未被释放,导致内存占用持续增长,引发性能下降或崩溃。
常见的内存泄漏有:
1.闭包使用不当,用全局变量声明了一个闭包,如果该页面不关闭的话,闭包里面的变量一直在占用内存。所以说使用完闭包后要及时解除闭包的引用。
2.未清除定时器,比如说设置了一个定时器,没设置清理定时器的代码,当程序执行时定时器就会一直占用内存造成内存泄漏。所以说使用完定时器后要使用clearnInterval进行清除定时器。
3.意外的全局变量,比如说一个变量没有进行声明,在非严格模式下就会自动变成全局变量,这种变量对内存的占用会持续到页面关闭,无法被回收。所以说要开启严格模式 use strict
4.残留的DOM引用,DOM节点被删除后,仍然有js变量去引用,就不会被垃圾回收,内存被占用。所以说严谨一点就需要在删除完获取的dom元素后让dom=null就可以释放内存了。