垃圾回收 Garbage Collection
2022-11-15 09:38:50
js 是使用垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存。这个过程是周期性的,每隔一段时间就会运行一次垃圾回收程序
两种主要的标记策略
- 标记清理
- 引用计数
标记清理
js 中最常用的垃圾回收策略。当变量进入上下文或者声明一个变量时,这个变量就会被加上存在于上下文中的标记,当变量离开上下文时也会被加上离开上下文的标记,给变量加标记的方式很多,标记过程的实现并不重要,重要的是策略
垃圾回收程序运行时会标记内存中存储的所有变量,然后把所有在上下文中的变量和被在上下文中的变量引用的变量的标记去掉,剩下有标记的变量就是待删除的了,因为不会再被访问到了,随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存
引用计数
引用计数(不常用)的思路是对每个值记录它被引用的次数,例如声明了一个变量后并赋了一个引用值时,它的计数为 1,又被赋给了另一个变量后计数+1,如果引用被其它变量覆盖了那么就-1,当一个变量的计数为 0 时就说明访问不到这个变量了,就可以安全的回收,垃圾回收程序下次运行时就会释放计数为 0 的值的内存
引用计数策略有一个严重的问题:循环引用 比如
js
function fun() {
const a = new Object()
const b = new Object()
a.a = b
b.b = a
}
1
2
3
4
5
6
7
2
3
4
5
6
7
a 和 b 互相引用,计数都为 2,那么永远不会被回收,如果这个函数被多次执行,那么就会创建多个变量占用内存 可以通过为属性值赋为 null 来切断连接
内存泄露
js 中的内存泄露大部分是由不合理的引用导致的
比如 如果定时器一直运行,那么回调函数中引用的 name 就会一直占用内存
js
let name = 'zhangsan'
setInterval(() => {
console.log(name)
}, 100)
1
2
3
4
2
3
4
在 js 中使用闭包也很容易造成内存泄露,在调用 fun 函数后会创建一个内部闭包,只要返回的函数存在就会导致 name 的内存泄露,因为闭包一直引用着 name
js
function fun() {
let name = 'zhangsan'
return function () {
return name
}
}
1
2
3
4
5
6
2
3
4
5
6