用代码实现JavaScript的垃圾回收机制

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

说到js的垃圾回收机制,我们可能没有感觉到,每次写代码的时候明什么都没有做说到js的垃圾回收机制,我们可能没有感觉到,每次写代码的时候明什么都没有做,那js的回收是怎么做到的呢?

内存的生命周期

  • 内存分配:当我们在声明变量、函数、对象的时,系统会自动的为他们分配内存
  • 内存使用:即读写内存、也就是使用变量、函数等
  • 内存回收:使用完毕,由垃圾回收自动回收不在使用的内存(全局变量一般不会回收,一般局部变量的值,不再使用,会自动回收掉)

内存分配

// 为变量分配内存
let a = 11
let b = 'wya'
//为对象分配内存
let obj = {
name: '小王',
age: '23'
}
//为函数分配内存
function fn(a, b) {
return a + b
}
复制代码

垃圾回收算法

垃圾回收的核心思想是判断内存是否已经不再使用了,如果是就视为垃圾,释放掉。 主要用的方法是引用计数和标计计数法

早期采用的主要是引用计数法,现在大多数都是标记清除法(IE到现在还是采用引用计数法)

咱们用代码举个列子

let obj = {
name: '小王',
age: 23
}

let p = obj
复制代码

image.png 这里这个堆里的复杂数据有两个引用者,所以它的引用计数就就为2,所以它是不会被js回收的。

然后咱们给obj和p赋予新的值,不让它俩继续引用这个复杂的数据

obj = 1
p = null
复制代码

image.png 这个时候她们就中断了对那个复杂数据的引用,引用计数的结果就为零, 这个时候就会被js认为无用的数据,会被回收!而1和null因为有obj和p的引用,所有它俩的引用计数都为1,js只会回收引用计数为0的数据

引用计数算法虽然是一个简单有效的算法,但是有一个很大的问题循环引用,用代码演示一下:

function fn() {
let a = {}
let b = {}
a.name = b
b.name = a
}
fn()
复制代码

这里当fn函数调用走完后,我们在全局访问函数里面变量是访问不到的, 所以函数走完后它里面声明的变量应该被回收,如果没被回收销毁,我们每执行这个函数一次都会在内存中开辟一个对象,这会对内存的极大的浪费!

这里如果使用的引用计数法, 函数里的变就不会被回收,画个图演示一下

image.png 如果函数里面只是声明了两个空对象的话,我们在全局访问不到,当这个函数走完的时候变量会销毁, 引用也就不存在了,数据就会被回收

image.png

当a和b两个对象里的属性互相引用时对方时,即使a和b销毁没有在引用数据,但数据是相互引用的,它们的*引用计数永远都会大于等于1, js就不会回收这个内存数据,这就是循环引用(访问不到,也不释放)

所以就出现了下面要说的这个标记清除法

  • 标记清除算法将"不在使用的对象"定义为"无法达到的对象"。
  • 简单来说,就是从根部(js中就是全局对象)出发定义时扫描内存中的对象。
  • 凡是能从根部到达的对象,都是还需要使用的。那些无法从根部出发触及到的对象被标记为不再使用,会被回收。

总结:从js根部(全局对象)出发,能够访问到的对象,普通变量、函数等,都是需要用的,都不会被释放。但是如果从js根部无法访问到,无法触及(谁都找不到这块空间),这块空间就是垃圾,需要被回收

猜你喜欢

转载自juejin.im/post/7086815060932165669
今日推荐