js-垃圾回收

垃圾回收

JavaScript 中的内存管理是自动执行的,而且是不可见的。

由于字符串,对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript 程序每次创建字符串、数组或对象时,解释器必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够再用,否则,JavaScript 的解释器将会消耗完系统中所有可用内存,造成系统崩溃

JavaScript 垃圾回收很简单:找到不再使用的变量,然后释放掉其占用的内存。但是这个过程不是实时的,因为开销比较大,所以垃圾回收器会按照固定的时间间隔周期性执行。

内部算法

标记清除

基本的垃圾回收算法称为 “标记-清除“,定期执行以下 “垃圾回收” 步骤

  • 垃圾回收器获取根并 “标记“ 他们

  • 然后它访问并 标记 所有来自他们的引用

  • 然后它访问标记的对象并标记他们的引用(所有被访问的对象都被记住,以便以后不再访问同一对象两次)

  • 以此类推,直到有未访问的引用(可以从根访问)为止

  • 除标记的对象外,所有对象都被删除

垃圾回收器只在 CPU 空闲时运行,以减少对执行的可能影响

引用计数

在 JavaScript 引擎中有一个引用表,保存着内存里面所有的资源的引用次数。如果一个值的引用次数是 0,就表示这个值不再用到了,因此就可以将这块内存释放掉

如果一个值不再需要了,引用数却不为 0,垃圾回收机制无法释放这块内存,从而导致内存泄漏

// 循环引用
let obj = {}
let obj1 = {}

obj.a = obj1
obj1.a = obj1

要解决循环引用的问题,最好在不使用他们的时候手工将他们设置为空

obj = null
obj1 = null

哪些情况会引起内存泄漏

意外的全局变量

未被声明的变量会变成一个全局变量,在页面关闭之前不会被释放。

function fn() {
  a = 1
}

function fn2() {
  this.a = 2
}

被遗忘的计时器或回调函数

闭包

闭包可以维持函数内部的变量,使其得不到释放。

function bindEvent() {
  var div = document.createElement('div')
  div.onclick = function() {}
}

解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对 dom 的引用

// 将事件函数定义在外边
function onClickEvent() {
  // ...
}

function bindEvent() {
  var div = document.createElement('div')
  div.onclick = onClickEvent
}
// 删除对 dom 的引用
function bindEvent() {
  var div = document.createElement('div')
  div.onclick = function() {
    // ...
  }
  div = null
}

垃圾回收优化

数组优化

在清空一个数组项的时候优先使用.length = 0 的方式去清空数组,如果使用 =[] 的方式会将原来的数组对象变成一个内存垃圾

对象尽量复用

对象尽量复用,不用的对象尽可能设置为 null

var t = {}
for (let i = 0; i < 5; i++) {
  t.age = i++
  t.name = 'name' + i
  // 这里使用t,使用完之后不用的话可以置为null
}

t = null

最后更新于