垃圾回收机制

186 阅读4分钟

垃圾回收机制

目标

  • 清除不在使用的对象,腾退出所占内存
  • 例如:当我们定义一个变量let person = { name: 'lili' },我们给它重新赋值一个新的对象后person = { name = 'sasa' },原来的对象就不再使用了,我们就可以把它清除掉({ name: 'lili' })。

策略

垃圾回收机制的策略分为:

  1. 引用计数法
  2. 标记清除法

引用计数法

  • 定义:当一个对象被引用一次,引用数+1,被取消引用一次,引用数-1,当引用数为 0 时,就可以触发垃圾 回收机制回收。
  • 缺陷:在循环引用的时候,引用次数永远不可能为 0 ,无法回收,内存无法释放

标记清除法

  • 定义:首先,我们将所有的对象标记为 0 ,从根对象开始遍历,将存活的对象标记为 1 ,然后将标记为 0 的对象清除掉,最后将所有标记为 1 的对象,重新标记为 0 ,方便下一次垃圾回收机制回收。
  • 缺陷:将标记为 0 的对象清除掉之后,对象的位置是不连续的,如果进来一个新对象,找位置就是一个新的问题
  • 解决:如何为新对象找合适位置?

如何为新对象找合适位置

找合适位置,有三种策略:

  1. First-fit:找到能放置下新对象的第一个块位置(性能最好,现在采用的主要策略)
  2. Best-fit:找到能放置下新对象的最小的块
  3. Worst-fit:找到最大的块,切一块空间给新对象(但会产生更多不连续的空间)

V8对垃圾回收机制的优化

优化标记算法

首先优化了标记算法,我们发现有的对象需要频繁回收,而有的对象不需要频繁回收。一般小的新的存活时间短的对象,需要频繁回收。而大的老的存活时间长的对象不需要频繁回收。所以在堆内存中分出两个区域来,一个是新生代区域,一个是老生代区域,新生代区域一般存放小的新的存活时间短的对象,老生代区域一般存放大的老的存活时间长的对象。

  • 新生代(小的新的存活时间短)垃圾回收机制:
    • 新生代区域,我们分出两个空间,一个from空间(使用空间),一个to空间(闲置空间),当from空间中的对象要满的时候,我们就开始标记,将存活的对象标记好,标记好后将它们复制到闲置空间中,最后再把from空间清空,然后再交换他们的名称(from变to,to变from)。
  • 老生代(大的老的存活时间长)垃圾回收机制:
    • 策略是之前提到的标记清除算法和标记压缩算法,首先将所有的对象标记为0,然后将存活的对象标记为1,将标记为0的对象全部清空,然后将标记为1的对象全部标记为0,最后再用标记压缩算法,使他们的位置整理好。

增量标记

因为JS是单线程,所以,在执行垃圾回收机制的时候,JS执行会被暂停,我们叫它全停顿。V8机制对这种情况做了一定的优化,垃圾回收机制支持多线程,并行回收,所以这样的话,使垃圾回收机制的时间加快了,时间变短,最大化的保证JS执行。但这样依旧会阻塞JS执行,所以就诞生了增量标记。

  • 增量标记最大特点:垃圾回收可以分段执行,也就是说,执行一段JS就执行一段垃圾回收机制,它们可以交叉执行,最大化的保证了JS执行,但是有一个新的问题,如何记录上一次标记的位置呢?所以诞生了三色标记法。

增量标记

当涉及到大型的垃圾回收操作时,如清除大量的内存对象或执行长时间的标记阶段,传统的标记清除算法可能会导致明显的应用程序停顿。为了减少这种停顿时间,增量标记算法被引入。

增量标记算法是一种将垃圾回收操作与应用程序交替执行的方式。它将标记阶段分解为多个小步骤,在每个步骤之间插入应用程序的执行。这样,垃圾回收操作和应用程序的执行交替进行,以减少单次垃圾回收操作对应用程序的影响。

以下是增量标记算法的基本步骤:

  • 初始标记(Initial Mark):在这个阶段,垃圾回收器会暂停应用程序的执行,然后从根对象开始,标记所有直接可达的对象。这个阶段的目标是快速标记出活动对象,以避免将它们误判为垃圾对象。

  • 并发标记(Concurrent Mark):在这个阶段,垃圾回收器会与应用程序交替执行。它会在应用程序执行的空闲时间段内,继续标记未被标记的对象。这样,垃圾回收操作和应用程序的执行可以同时进行,减少了应用程序的停顿时间。

  • 重新标记(Remark):在并发标记阶段结束后,垃圾回收器会再次暂停应用程序的执行,完成标记过程。它会检查在并发标记期间可能发生变化的对象,并进行必要的调整和修正。

  • 清除(Sweep):在重新标记阶段完成后,垃圾回收器会执行清除操作,释放被标记为垃圾对象的内存空间。这个阶段与传统的标记清除算法相似,但由于增量标记算法的使用,清除阶段的时间通常较短。

增量标记算法的优点是可以将垃圾回收操作分散到多个小步骤中,与应用程序的执行交替进行,从而减少了单次垃圾回收操作对应用程序的影响。这种方式适用于大型的内存堆和需要长时间标记阶段的场景,可以显著减少应用程序的停顿时间,提高用户体验。

然而,增量标记算法也存在一些缺点。由于垃圾回收器与应用程序交替执行,增加了算法的复杂性,可能导致更多的开销。此外,增量标记算法可能会引入一些额外的内存开销,因为需要记录对象的标记状态。

总体而言,增量标记算法是一种在大型垃圾回收操作中减少应用程序停顿时间的有效方式,但需要权衡算法的复杂性和额外的内存开销。

三色标记法

三色标记法就是将上次标记到的位置,我们标记为灰色,当下次执行的时候,从灰色开始重新标记。

并发回收

同时V8机制还支持了并发回收,也就是说,JS在主线程执行,垃圾回收在辅助线程执行。