持续创作,加速成长!这是我参与「掘金日新计划 · 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引擎