# 官方博客

2019.05.301

作为编写程序的人,是可以做出“这个对象已经不再需要了”这样的判断,但计算机是做不到的。因此,如果程序(通过某个变量等等)可能会直接或间接地引用一个对象,那么这个对象就被视为“存活”;与之相反,已经引用不到的对象被视为“死亡”。将这些“死亡”对象找出来,然后作为垃圾进行回收,这就是 GC 的本质。

三大基础 GC 算法

https://juejin.im/entry/5d2ee41af265da1b7004defd

# 引用计数法

它的基本原理是,在每个对象中保存该对象的引用计数,当引用发生增减时对计数进行更新。引用计数的增减,一般发生在变量赋值、对象内容更新、函数结束(局部变量不再被引用)等时间点。当一个对象的引用计数变为 0 时,则说明它将来不会再被引用,因此可以释放相应的内存空间。

引用计数最大的缺点,就是无法释放循环引用的对象。

# 标记清除法/标记压缩法

这个算法假定设置一个叫做根(root)的对象(在 Javascript 里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象

从 2012 年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对 JavaScript 垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。

标记清除算法有一个缺点,就是在分配了大量对象,并且其中只有一小部分存活的情况下,所消耗的时间会大大超过必要的值,这是因为在清除阶段还需要对大量死亡对象进行扫描。

三色标记 大部分使用该算法。现代 JS 引擎的垃圾回收算法都已经不是简单的活跃与否的 mark

  • 状态为白(white), 它尚未被垃圾回收器发现
  • 状态为灰(gray), 它已被垃圾回收器发现,但它的邻接对象仍未全部处理完毕
  • 状态为黑(black), 它不仅被垃圾回收器发现,而且其所有邻接对象也都处理完毕

# 复制收集算法

在这种算法中,会将从根开始被引用的对象复制到另外的空间中,然后,再将复制的对象所能够引用的对象用递归的方式不断复制下去。

但是,和标记相比,将对象复制一份所需要的开销则比较大,因此在“存活”对象比例较高的情况下,反而会比较不利。这种算法的另一个好处是它具有局部性(Lo-cality)。在复制收集过程中,会按照对象被引用的顺序将对象复制到新空间中。于是,关系较近的对象被放在距离较近的内存空间中的可能性会提高,这被称为局部性。局部性高的情况下,内存缓存会更容易有效运作,程序的运行性能也能够得到提高。

# 分代回收

V8 引擎将保存对象的 堆 (heap) 进行了分代:

  • 对象最初会被分在 新生区(New Space) (1~8M),新生区的内存分配只需要保有一个指向内存区的指针,不断根据内存大小进行递增,当指针达到新生区的末尾,会有一次垃圾回收清理(小周期),清理掉新生区中不再活跃的死对象。

  • 对于超过 2 个小周期的对象,则需要将其移动至 老生区(Old Space)。老生区在 标记-清除 或 标记-紧缩 的过程(大周期) 中进行回收。

大周期进行的并不频繁。一次大周期通常是在移动足够多的对象至老生区后才会发生

参考: