前端问题清单——JavaScript的垃圾回收

152 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

作为JavaScript的开发者,在创建对象、原始值、函数的时候,不用像c那样需要关心需要申请多少内存,什么时候去释放内存,JavaScript都会自动处理。但是,项目中还是会有各种内存泄漏的情况出现,所以,很有必要了解JavaScript是怎么垃圾回收的。

JavaScript内存管理是否需要释放内存的依据是这个值是否可访问

不能被释放的根的集合

  • 当前执行的函数,以及这个函数所包括的局部变量和参数
  • 当前嵌套调用链的其他函数以及这些函数所包括的局部变量和参数
  • 全局变量

判断是否可访问

  • 一个值可以通过引用或引用链从根访问到,那认为这个值是可访问的

JavaScript引擎在后台运行了垃圾回收器,监控所有值的状态,并且删掉那些不可访问的

简单的引用

let cat = {
    voice:'mew'
}

cat是对象{voice:'mew'}的引用,把对象称为C

cat = {name:'Tom'}

1、如果cat被重写,对C的引用就消失了,此时内存中的C就不能被访问了,垃圾回收器会任务C是垃圾数据进行回收

let kitty = cat

2、如果把cat的值赋值给kitty,kitty也成了C的引用,此时再重写cat,C依然可以被访问,所以对象还在内存中,这个时候如果再重写kitty,对象才会被删除

复杂的引用

function relation(cow,grass){
    cow.food = grass
    reeturn {
        consumer:cow,
        producer:grass
    }
}
let relationship = relation({name:'cow'},{name:'grass'})

relation函数让两个对象之间产生了关系,并且返回了一个包含两个对象的新对象

1、如果移除了一个引用

delete relationship.producer

此时所有的对象都是可访问的,grass依然可以通过cow访问,因此删除这一个引用是不够的

delete relationship.consumer.food

要把两个都删除,此时才访问不到对象{name:'grass'},从内存中删除掉。 但是此时还是可以通过relationship.consumer访问对象{name:'cow'}

2、如果重写了relationship

relationship = null

此时cow和grass虽然还有关系,但是外界没有引用可以访问内部的两个对象,因此这两个对象会从内存中删掉

垃圾回收的内部算法

  • 找到所有的根,并且标记它们
  • 遍历根然后标记根的引用
  • 遍历所有被标记的对象并且标记它们的引用
  • 循环以上操作,知道所有的从根开始的可访问的引用都被访问到,从而被标记
  • 如果没有标记的对象就会被删除

一些垃圾回收的优化

  • 分代收集
    • 对象被分成两组,新的和旧的。有些对象的出现在完成相关的操作后很快就可以被清理,那些长期存活的对象会被分到旧的,检查的频率会减少
  • 增量收集
    • 如果有很多的对象,一次性的标记所有的对象需要耗费一些时间带来一些明显的延迟,所以引擎会把垃圾收集工作分成几部分,每个部分逐一处理,这样就需要额外的标记来追踪变化,把一个大的延迟分几个小延迟
  • 闲时收集
    • 垃圾收集器只在CPU空闲的时候运行,减少对代码执行的影响

总结

  • 垃圾回收是自动完成的
  • 如果一个对象可以访问,那么一定是存在于内存中的
  • 如果要学习垃圾收集,可以参照V8引擎