跳到主要内容

垃圾回收

简介

JavaScript 属于解释性语言,需要在代码执行时,将代码编译为机器码。

垃圾回收,又称为 GC(garbage collection)。垃圾回收器会监视内存,当内存中的对象不再被引用时,就会回收内存。

判断是否是垃圾可以看「可达性」,就看能不能访问到它。

例如下面给 a 重新赋值,那么 a 指向的对象{ name: 'gc' }不再被引用,就会被回收。

let a = { name: 'gc' };
a = [1, 2, 3];

垃圾回收算法

  • 引用计数算法
  • 标记清除算法(mark-sweep)

引用计数算法

跟踪记录每个变量值被引用的次数

  • 当声明了一个变量并且将一个引用类型赋值给该变量的时候,这个值的引用次数就是 1
  • 如果同一个值又被赋给另一个变量,那么引用次数加 1
  • 如果该变量的值被其他的值覆盖,那么引用次数减 1
  • 当这个值的引用次数变为 0 的时候,说明没有变量在使用,这个值没法访问了,可以被回收

缺点:无法解决「循环引用」无法回收的问题

function foo() {
let A = new Object();
let B = new Object();
A.b = B;
B.a = A;
}

标记清除算法

标记出所有被引用的对象,然后清除未被标记的对象

最常用的清除算法。

当变量进入执行环境时,反转某一位(通过一个二进制字符来表示标记,0 表示未被标记,1 表示被标记),又或者可以维护进入环境变量和离开环境变量这样两个列表,可以自由的把变量从一个列表转移到另一个列表。

引擎在执行 GC 时,需要从出发点去遍历内存中所有的对象去打标记,而这个出发点有很多,称之为一组「根对象」。根对象在浏览器中包括又不止于全局 Window 对象、文档 DOM 树。

大致过程:

  1. 垃圾收集器在运行时会给内存中的所有变量都加上一个标记,假设内存中所有对象都是垃圾,全部标记为 0
  2. 然后从各个根对象开始遍历,把不是垃圾的节点改为 1
  3. 清理所有标记为 0 的垃圾,销毁并回收他们所占用的内存空间
  4. 最后把所有内存中的对象标记修改为 0,等待下一轮垃圾回收

缺点:在清除之后,剩余的对象内容位置是不变的,会导致空闲内存空间是不连续的,出现了「内存碎片」,就会出现关于内存分配的问题。