V8

  • V8 是 Google 开源的高性能 JavaScriptWebAssembly 引擎,用 C++ 编写。它用于 ChromeNode.js 等。
  • V8 实现了 ECMAScriptWebAssembly,并在 Windows 7 或更高版本、macOS 10.12+ 以及使用 x64IA-32ARMMIPS 处理器的 Linux 系统上运行。
  • V8 可以独立运行,也可以嵌入到任何 C++ 应用程序中。
  • V8 编译执行 JavaScript 源代码,处理对象的内存分配,并对不再需要的对象进行垃圾回收
  • V8 中的 stop-the-worldgenerational(分代)准确垃圾收集器V8 高性能的关键之一。

Garbage Collector

  • Garbage Collector: 垃圾回收器, 简称 GC;

  • 任何垃圾收集器都有一些必须定期执行的基本任务:

    • 识别活/死对象;
    • 回收/重用死对象占用的内存;
    • 压缩/整理内存碎片(可选);
  • 这些任务可以按顺序执行,也可以任意交错执行。一种直接的方法是暂停 JavaScript 执行并在 主线程 上按顺序执行这些任务, 但是这可能会导致主线程出现卡顿延迟问题;

在主线程上执行的垃圾收集

V8 引擎中的垃圾回收器是如何工作的呢?

在了解V8 引擎中的垃圾回收器的工作机制之前,我们应该先了解一下 V8 中的 内存布局,即:Generational layout(分代布局)

Generational layout

  • V8 中的堆内存被分成不同的区域,称为generations(代)
  • 其中一个 young generation(年轻代) 和 一个 old generation(老一代)
  • young generation(年轻代) 中又分为了 nursery and intermediate sub-generations;
  • V8 中的堆内存被分成几代。当对象在 GC 中幸存下来时,它们会被代代相传。

V8 堆被分成几代, 当对象在 GC 中幸存下来时,它们会代代相传。

The Generational Hypothesis

  • The Generational Hypothesis(世代假设),它是垃圾收集中一个重要的术语;
  • 从 GC 的角度来看,大多数对象都已分配,然后立即变得不可访问。只有极少数的对象在垃圾收集中幸存下来;
  • 这不仅适用于 V8 或 JavaScript,而且适用于大多数动态语言。

V8 Garbage Collector

  • V8 中有两个垃圾收集器, 分别是 Major GC (Mark-Compact)Minor GC (Scavenger)
  • Major GC (Mark-Compact)整个堆内存(Heap)中收集垃圾;
  • Minor GC (Scavenger)Young Generation(年轻代)收集垃圾;

Major GC (Mark-Compact)

Marking(标记)

  • 确定可以收集哪些对象是垃圾收集的重要组成部分;
  • 垃圾收集器通过使用可达性作为活跃度的依据来做到这一点;
  • Marking(标记)是发现可达对象的过程;
    • GC 从一组已知对象指针开始,称为根集。这包括执行堆栈和全局对象。
    • 然后它跟随每个指向 JavaScript 对象的指针,并将该对象标记为可访问。
    • GC 跟踪该对象中的每个指针,并递归地执行此过程,直到找到并标记运行时中可访问的每个对象。

Major GC 分三个阶段进行:标记、清扫和压缩。

Sweeping(清扫)

  • 标记完成后,GC 会发现无法访问的对象留下的连续间隙,并将它们添加到适当的free-list(空闲列表)中;
  • free-list(空闲列表)由内存块的大小分隔,以便快速查找。
  • 将来当我们想要分配内存时,我们只需查看free-list(空闲列表)并找到适当大小的内存块。
    sweep to free list

Compaction(压缩/整理)

  • 将幸存的对象复制到当前未压缩的其他页面中(使用该页面的空闲列表),这样我们就可以利用死对象留下的小而分散的内存碎片。
  • 复制幸存对象有一个潜在弱点:当我们分配大量长寿命对象时,我们为复制这些对象付出了高昂的代价。因此。我们只选择一些高度碎片化的页面进行Compaction(压实/整理),而其他页面只执Sweeping(清扫),这不会复制幸存的对象。
    sweep to free list

Minor GC (Scavenger)

  • Scavenger 中,幸存的对象总是被复制到新的页面;
  • V8 为年轻代使用了semi-space(半空间)的设计。这意味着总空间的一半始终是空的,以允许进行上述复制步骤;
  • 复制步骤将所有幸存的对象移动到一个连续的内存块(在一个页面内)中。这样做的好处是消除内存碎片(死对象留下的间隙)
    step 1
    step 2
    step 3
    step 4
    step 5
    step 6
    step 7

minorg-scavenge

Orinoco

  • OrinocoV8 引擎中的垃圾回收器的项目代号;
  • Orinoco 是一个采用了sequential(顺序)stop-the-world垃圾回收器;
  • Orinoco 是一个大部分 parallel(并行)concurrent(并发)垃圾回收器,并带有 增量回退;