浏览器的垃圾回收机制

220 阅读4分钟

浏览器的垃圾回收机制

js代码运行的时候,浏览器需要分配内存空间来存储变量和值的。当变量不参与运行时,就需要回收被占用的内存空间,这就是垃圾回收

回收机制

  • js具有自动垃圾回收机制,会定期对那些不再使用的变量、对象所占用的内存进行释放,浏览器的垃圾回收机制
  • js中存在两种变量:局部变量和全局变量。全局变量的生命周期会持续到页面卸载;而局部变量存在于函数中,它的生命周期是从函数执行开始,直到函数执行结束,这些局部变量不再被使用给,它们所占用的空间就会被释放。
  • 还有一种特殊的情况,那就是闭包了,在函数结束后,函数外部的变量依然被函数里面引用着,所以不会被回收

(2)垃圾回收的方式

浏览器常用的垃圾回收方法有两种:标记清除、引用计数

(1) 标记清除

  • 标记清除是浏览器最常见的垃圾回收方式,其原理是当变量进入执行环境时,就标记这个变量“进入环境”,被标记为“进入环境”的变量时不能被回收的,因为它们正在被使用。当变量离开环境的时候,就会被标记为“离开环境”,被标记为“离开环境”的变量会被内存释放

  • 垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后会对环境中的变量以及被环境中的变量引用的标记去掉。此后再被加上标记的变量将会被看做准备要删除的变量,最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并返回它们所占用的内存空间

(2)引用计数

  • 另一种的垃圾回收机制就是引用计数,这个用的相对较少。其原理是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。当包含这个的值引用的变量又取得了另一个值,则这个值的引用次数就减1。直到这个引用次数变为0的时候,说明这个变量已经没有价值了,在垃圾回收下次运行的时候,这个变量所占用的内存空间就会被释放。

  • 这种方法的缺点是会引起循环引用的问题:例如:

function fn(){
  let obj1 = {}
  let obj2 = {}
  obj1.a = obj2   // obj1 引用 obj2
  obj2.a = obj1   // obj2 引用 obj1
}

由于obj1obj2通过属性进行相互引用,两个对象的引用次数都是2,当函数执行结束的时候,obj1obj2还将会继续存在,因此它们的引用次数永远不会是0,也就是无法被垃圾回收,这种情况需要手动释放变量所占用的内存

obj1.a = null
obj2.a = null

(3)减少垃圾回收

虽然浏览器可以进行垃圾自动回收,但是当代码比较复杂时,垃圾回收所带来的代价比较大,所以应该尽量减少垃圾回收。

  • 对数组进行优化:  在清空一个数组时,最简单的方法就是给其赋值为[],但是与此同时会创建一个新的空对象,可以将数组的长度设置为0,以此来达到清空数组的目的。
  • object进行优化:  对象尽量复用,对于不再使用的对象,就将其设置为null,尽快被回收。
  • 对函数进行优化:  在循环中的函数表达式,如果可以复用,尽量放在函数的外面。

(4)哪些情况会导致内存泄漏

  • 意外的全局变量:  由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
  • 被遗忘的计时器或回调函数:  设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
  • 脱离 DOM 的引用:  获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
  • 闭包:  不合理的使用闭包,从而导致某些变量一直被留在内存当中。