垃圾回收GC
- 什么是垃圾回收?
function fn1() {
const a = 'aa'
console.log(a)
const obj = { x: 100 }
console.log(obj)
}
fn1() // 一边执行一边垃圾回收
function fn2() {
const obj = { x: 100 }
window.obj = obj // 符合用户预期,不称为内存泄漏
}
fn2() // 执行完后依旧有,回收不了
function getDataFns() {
const data = {} // 闭包 常驻内存不会被销毁
return {
get(key) {
return data[key]
},
set(key, value) {
data[key] = value
},
}
}
const { get, set } = getDataFns()
set('x', 100)
get('x')
引用计数(之前)
// 对象被a引用
let a = {
x: 100,
}
let a1 = a
a = 10
a1 = null // 此时上面对象的引用数为0 被销毁
// 缺陷 循环引用
function fn3() {
const obj1 = {}
const obj2 = {}
obj1.a = obj2
obj2.a = obj1
}
fn3()
// IE6-7 内存泄漏的bug
var div1 = document.getElementById('div1')
div1.a = div1
div.someBigData = {} // 非预期的内存泄漏
标记清除(现代)
- 从js的根(window)遍历各个属性,看看能不能得到某个对象,如果得到,保留,得不到,则删除
闭包是内存泄漏吗?
- 闭包严格来说不算内存泄漏
- 内存泄漏是一种非预期的情况
- 闭包符合预期
检测内存变化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=
, initial-scale=1.0"
/>
<title>Document</title>
</head>
<body>
<p>
memory change
<button id="btn1">start</button>
</p>
<script>
const arr = []
for (let i = 0; i < 10 * 10000; i++) {
arr.push(i)
}
function bind() {
// 模拟比较大的数据
const obj = {
str: JSON.stringify(arr), // 简单的拷贝
}
window.addEventListener('resize', () => {
console.log(obj)
})
// 不会销毁 因为是全局引用
}
let n = 0
function start() {
setTimeout(() => {
bind()
n++
if (n < 50) {
start()
} else {
alert('done')
}
}, 200)
}
document.getElementById('btn1').addEventListener('click', () => {
start()
})
</script>
</body>
</html>
操作过程:
然后点击start:
出现alert弹框后,点击stop,观测数据
可以看到内存从4mb一直飙升到了33mb,验证无法进行垃圾回收带来的结果
无法销毁bind绑定的数据,内存随着时间持续增大
内存泄漏的场景(以vue为例)
- 被全局变量、函数引用,组件销毁时未清除
- 被全局事件、定时器引用,组件销毁时未清除
- 被自定义事件引用,组件销毁时未清除
全局变量清除内存泄漏风险
定时器清除内存泄漏风险
全局事件清除内存泄漏风险
自定义事件清除内存泄漏风险
划重点
- 前几年前端不太注重内存泄漏,因为不像后端7*24小时运行
- 近几年前端功能不断复杂,内存问题也要重点考虑
扩展:WeakMap WeakSet
// 标记清除
const data = {}
function fn1() {
const obj = { x: 100 }
data.obj = obj // 被全局变量引用不能垃圾回收
}
fn1()
const map = new Map()
function fn2() {
const obj = { x: 100 }
map.set('a', obj) // 全局引用
}
fn2()
const wMap = new Map() // 弱引用
function fn3() {
const obj = { x: 100 }
wMap.set(obj,100) // 弱引用 weakMap的key只能是引用类型 留不住x=100这个数据,会被清理掉
}
fn3()