JavaScript 内存管理之垃圾回收
Contents
V8
V8
是 Google 开源的高性能JavaScript
和WebAssembly
引擎,用C++
编写。它用于Chrome
和Node.js
等。V8
实现了ECMAScript
和WebAssembly
,并在 Windows 7 或更高版本、macOS 10.12+ 以及使用x64
、IA-32
、ARM
或MIPS
处理器的 Linux 系统上运行。V8
可以独立运行,也可以嵌入到任何 C++ 应用程序中。V8
编译
和执行
JavaScript
源代码,处理对象的内存分配
,并对不再需要的对象进行垃圾回收
。V8
中的stop-the-world
、generational(分代)
、准确
的垃圾收集器
是V8
高性能的关键之一。
Garbage Collector
Garbage Collector
: 垃圾回收器, 简称GC
;任何
垃圾收集器
都有一些必须定期
执行的基本任务:- 识别活/死对象;
- 回收/重用死对象占用的内存;
- 压缩/整理内存碎片(可选);
这些任务可以按顺序执行,也可以任意交错执行。一种直接的方法是暂停
JavaScript
执行并在主线程
上按顺序执行这些任务, 但是这可能会导致主线程
出现卡顿
和延迟
问题;
那
V8
引擎中的垃圾回收器是如何工作的呢?
在了解V8
引擎中的垃圾回收器的工作机制之前,我们应该先了解一下 V8
中的 内存布局
,即:Generational layout(分代布局)
Generational layout
V8
中的堆内存
被分成不同的区域,称为generations(代)
。- 其中一个
young generation(年轻代)
和 一个old generation(老一代)
。 - 在
young generation(年轻代)
中又分为了nursery
andintermediate
sub-generations; 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
跟踪该对象中的每个指针,并递归地执行此过程,直到找到并标记运行时中可访问的每个对象。
Sweeping(清扫)
- 标记完成后,GC 会发现无法访问的对象留下的连续间隙,并将它们添加到适当的
free-list(空闲列表)
中; free-list(空闲列表)
由内存块的大小分隔,以便快速查找。- 将来当我们想要分配内存时,我们只需查看
free-list(空闲列表)
并找到适当大小的内存块。
Compaction(压缩/整理)
- 将幸存的对象复制到当前未压缩的其他页面中(使用该页面的空闲列表),这样我们就可以利用死对象留下的小而分散的内存碎片。
- 复制幸存对象有一个潜在弱点:当我们分配大量长寿命对象时,我们为复制这些对象付出了高昂的代价。因此。我们只选择一些
高度碎片化
的页面进行Compaction(压实/整理)
,而其他页面只执Sweeping(清扫)
,这不会复制幸存的对象。
Minor GC (Scavenger)
- 在
Scavenger
中,幸存的对象总是被复制到新的页面; - V8 为
年轻代
使用了semi-space(半空间)
的设计。这意味着总空间的一半始终是空的,以允许进行上述复制步骤; - 复制步骤将所有幸存的对象移动到一个连续的内存块(在一个页面内)中。这样做的好处是消除内存碎片(死对象留下的间隙)
Orinoco
Orinoco
是V8
引擎中的垃圾回收器
的项目代号;Orinoco
是一个采用了sequential(顺序)
、stop-the-world
的垃圾回收器
;Orinoco
是一个大部分parallel(并行)
和concurrent(并发)
的垃圾回收器
,并带有增量回退
;