JS垃圾回收

157 阅读3分钟

强烈建议看这篇英文文档

一、什么是垃圾

首先先理解所有变量都有生命周期,比如函数作用域结束后变量就变成了垃圾

  1. 没有被引用的匿名对象就是垃圾,除了下面这个例子
  2. 之所以说可能是因为有特例,那就是如果三个对象互相被引用,也只是他们仨之间互相被引用,形成一个环一个孤岛,如果想把环引用的其中一个对象变成垃圾,把这个对象被人的引用全部删掉,它就变成垃圾。如果想把整个环引用删掉那么只要去掉根部的引用就好
  3. 具体的
  • window不是垃圾
  • 所有全局变量/函数不是垃圾,因为你可能在任何地方用到
  • 一个函数中的局部变量在这个函数执行后就成了垃圾,但在执行期间始终存在,函数作用域里的变量为什么不被回收,因为被使用了
  • 单变量对象引用,也就是一个对象被一个变量引用了。在这个变量被重新赋值之后,这个对象就不被任何变量所引用了,这个对象就是垃圾了
  • 双变量对象引用,也就是一个对象被两个变量都引用了。如果其中一个变量被重新赋值了,那么这个对象还是被另一个变量引用,所以这个对象不是垃圾

二、垃圾回收算法

(一)标记清除算法

  • 从全局对象开始,把它引用的所有对象都标记一下。
  • 遍历所有被标记的对象,去标记他们引用的对象。
  • 以此类推,直到找不到新的可以被标记的对象。这样,标记过程就算结束了。
  • 可以开始清除了:把所有没有标记过的对象都清除掉。
  • 一个函数执行完之后马上遍历,回收垃圾

(二)引用计数 Reference Counting

引用计数,就是记录每个对象被引用的次数,每次新建对象、赋值引用和删除引用的同时更新计数器,如果计数器值为0则直接回收内存。 很明显,引用计数最大的优势是暂停时间短

  1. 优点
  • 可即刻回收垃圾
  • 最大暂停时间短
  • 没有必要沿指针查找, 不要和标记-清除算法一样沿着根集合开始查找
  1. 缺点
  • 计数器的增减处理繁重
  • 计数器需要占用很多位
  • 实现繁琐复杂, 每个赋值操作都得替换成引用更新操作
  • 循环引用无法回收

三、DOM

虽然js是单线程,但是还有一个dom线程

var div = document. getElementById('xxx')
div.onclick = function() {
   ///code
}
setTimeout(function(){
   div. remove()
}, 3000)

现在我们讨论,什么时候div上的函数被回收

函数被全局变量div上的onlick引用了

div.remove()只是在页面删掉了,没有被内存删掉

var div = document. getElementById('xxx')
div.onclick = function() {
   ///code
}
setTimeout(function(){
   div = mull
}, 3000)

这个函数并没有被删除,函数是写在dom上的,div变量只是引用了dom对象

var div = document. getElementById('xxx')
div.onclick = function() {
   ///code
}
setTimeout(function(){
   var div2 = document. getElementById('xxx')
}, 3000)

div= null和div.remove同时做就可以了,分别从内存和dom上删除了

ie有bug,即使这样都删不了,div.onlick = null 可以