SpiderMonkey 垃圾回收器

SpiderMonkey 垃圾回收器负责分配表示 JavaScript 数据结构的内存,并在它们不再使用时释放它们。它的目标是在尽可能短的时间内收集尽可能多的数据。除了 JavaScript 数据之外,它还用于分配一些 SpiderMonkey 内部数据结构。

垃圾回收器是一种混合追踪收集器,具有以下特性:

有关垃圾回收的概述,请参见:https://en.wikipedia.org/wiki/Tracing_garbage_collection

特性描述

精确收集

GC 是“精确的”,因为它知道分配的布局(用于确定可到达的子节点)以及所有栈根的位置。这意味着它不需要采用可能会导致垃圾被不必要地保留的保守技术。

栈的知识是通过 C++ 包装类实现的,这些类必须用于栈根和指向它们的句柄(指针)。这是由 SpiderMonkey API(它根据这些类型进行操作)强制执行的,并由静态分析检查,该分析报告当 GC 可能发生时存在未根节点的 GC 指针的位置。

有关栈根节点的详细信息,请参见:https://github.com/mozilla-spidermonkey/spidermonkey-embedding-examples/blob/esr78/docs/GC%20Rooting%20Guide.md

我们还有一个用于检测根节点错误的静态分析。它可以在本地或 CI 中运行

增量收集

“停止世界”收集器一次运行整个收集过程,这可能导致用户出现无法接受的暂停。增量收集器将其执行分解成多个小的片段,从而减少对用户的影响。

尽可能地,SpiderMonkey 收集器以增量方式运行。但是,并非所有收集部分都可以增量执行,因为某些操作需要相对于程序的其余部分原子地完成。

目前,大部分收集都是增量执行的。根节点标记、压缩和清扫的初始部分不是增量执行的。

分代收集

大多数现实世界的分配要么很快消失,要么存活很长时间。这表明了一种收集方法,其中分配根据它们存活的时间长短在“代”(单独的堆)之间移动。包含年轻分配的代收集速度很快,可以更频繁地收集;较老的代收集频率较低。

SpiderMonkey 收集器实现了一个年轻代(苗圃)和一个老年代(终身堆)。收集苗圃被称为次要 GC,而收集整个堆(包括苗圃)则被称为主要 GC。

并发收集

许多系统拥有多个 CPU,因此可以从将 GC 工作卸载到另一个内核中获益。在 GC 术语中,“并发”通常指的是在主程序继续运行时发生的 GC 工作。

SpiderMonkey 收集器目前仅在有限的阶段使用并发。

这包括大多数完成工作(有一些限制,因为并非所有完成代码都能容忍此操作)以及其他一些方面,例如分配和释放内存块。

目前正在研究并发执行标记工作。

并行收集

在 GC 术语中,“并行”通常意味着在收集器运行时并行执行的工作,而不是主程序本身。SpiderMonkey 收集器尽可能在 GC 片段内并行执行工作。

压缩收集

收集器在“区域”(通常称为板)中分配相同类型和大小的数据。在许多分配死亡后,这可能会导致许多区域包含空闲空间(外部碎片)。压缩通过在区域之间移动分配来解决此问题,以释放尽可能多的内存。

压缩涉及跟踪整个堆以更新指向已移动数据的指针,并且不是增量的,因此它很少发生,或者响应内存压力通知而发生。

分区堆

收集器具有“区域”的概念,它们是可以在独立收集的单独堆。

区域还用于帮助将收集的某些部分增量化。例如,压缩不是完全增量的,但可以一次执行一个区域。

其他文档

有关垃圾回收器 (GC) 的更多详细信息,可以通过在源代码中查找 [SMDOC] 垃圾回收器 注释来找到。