V8内存回收机制

V8 采用的垃圾回收算法(2019年)

内存回收机制的基本组成部分

  1. 识别存活/已死亡对象
  2. 回收死亡对象的内存
  3. 整合分片化内存(可选)

v8垃圾回收机制分为Major GCMinor GC两种内存回收器

  • Major GC:Mark Compact(针对整个堆空间)
  • Minor GC:Scavenge & Minor Mark Sweep(针对新生代)

Major GC (Full Mark-Compact)

major gc负责从整个堆内存中回收垃圾内存。

上图为最简单的版本,将Major GC 分为三个阶段: marking, sweeping和compacting,存在很大的延迟,影响性能。

Marking

通过可达性判断对象是否存活(可达:当前运行状态可引用的对象;不可达:其他所有不被引用对象)
首先从已知对象指针根集合(执行栈、全局对象)开始查找指针对应对象并将其标记为可达,然后递归遍历。
V8使用三色标记法来标记对象,每个对象通过两个标记位和一个标记列表来实现标记,两个标记为标识三种颜色:白色(00)、灰色(10)和黑色(11)。最初所有对象都是白色,当垃圾回收器发现白色物体并将其推送到标记列表时,它会变成灰色。当收集器从标记工作列表弹出对象并访问其所有字段时,灰色对象变为黑色。当不再有灰色对象时,标记完成,所有剩余白色物体都无法到达,可以安全地回收。

三色标记法
  • 黑色表示正在使用的对象,不能回收
  • 灰色表示通过黑色对象可以被访问的对象,需要加入worklist;在worklist的对象会在下一次迭代中标记为黑色
  • 白色表示在当前状态下,不能被访问到的对象
    GC算法标记结束后,白色对象将被释放。当前漏洞成因是一个能够被访问到的对象在GC算法结束时,被错误地标记为白色,导致被释放,出现UAF。
Sweeping

将死亡对象(不可达对象)添加到free-lists。每一段内存区域对应一个free-list,方便后续查找死亡对象进行释放以及重新申请内存。

Compaction

major gc在此阶段根据该内存页的分片情况选择是否进行Compact。
Compact策略是将分片比较严重的内存页中的存活对象复制到其他内存页,从而保证该内存页仅剩死亡对象,方便后续的内存回收。
弊端:需要复制大量的存活对象,因此仅当内存页分片比较严重时才会这样做。其他情况则仅做sweeping操作。

Major GC源码分析

Minor GC (Scavenger)

Generational layout

V8中的堆内存被分为不同的区域,称为“generations”。分为新生代(young generation,分为nursery和intermediate)和老生代(old generation)。
初始申请对象的堆内存时,将其申请在nursery空间。如果在下一次GC后,该对象仍然标记为存活,则将其移动到intermediate区域。如果再一次GC后,该对象仍然存活,则将其移动到老生代区域。(基于大部分申请对象的存活周期都比较短的现象)。

Cheney’s Semispace Copy

分为三步(Marking,Evacuating,Pointer-updating)
每次gc后存活对象会被迁移到新的内存页。使用”semi-space”设计,一半为全空(To-Space,intermediate),另一半为Nursery区域(From-Space)。
在scavenge过程中,需要额外维护一个根集合(Old-to-young generation references:老生代的指针引用新生代的对象)。使用remebered set、write-barriers来维护该集合。

When combined with the stack and globals, we know every reference into the young generation, without the need to trace through the entire old generation.

Marking:以调用栈以及old-to-young generation reference作为根集合开始递归搜索并移动。
Evacuation:将所有存活对象移动到一段连续内存,可以解决内存分片的问题。然后将两个空间翻转重新开始下一轮gc。

两轮都存活则移动到old-space。
Pointer-Updating: 最后更新指向移动过的对象的指针。每一个拷贝的对象都会遗留一个forwarding-address来更新原指针指向新地址。

Minor GC源码分析
Write-barriers

具体见[[2025-07-25-Write Barrier.md|Write-barriers]]

parallel Mark-Evacuate(Since V6.2)

基于Major GC的Mark-Sweep-Compact垃圾回收器实现。分为三步(Marking, Copying,Updating pointers)
没有使用sweep机制,仍然使用semispace机制来保证compact特性。
分为多个线程,分别负责一段内存区域;每一个阶段完成进入下一阶段前需要进行线程间同步。

parallel Scavenge

将parallel Mark-Evacuate的三个阶段合并起来,每个内存页维护remembered set。对象并行处理,新发现的对象被添加到global work list用于gc线程处理。write barrier确保了当前处理对象不适合gc处理时,不会过早终止
parallel Mark Evacuate与parallel Scavenge的区别见:[[2025-07-25-parallel Mark-Evaculate vs parallel Scavenge|Parallel Mark-Evaculate vs Parallel Scavenge]]

Orinoco(Google GC项目名称)

介绍了一些gc里面的名词

Parallel

主线程与其他工作线程一起完成gc工作:

Incremental

分为多个小阶段

Concurrent

交由工作线程处理,等结果返回后交付给主线程处理

垃圾回收机制的创新,哪些时候引入的

Jank Busters Part One(2015)

原始版本:Chrome 41 -> 46
优化前:

优化后:

Jank Busters Part Two(2016)

优化1:实现 generational garbage collectorparallel处理。
优化前:

优化后:

优化2:更新了跟踪指针方式(rememberd set),改变指针方向,避免指针重复以及race。

优化3:black allocation(Since V8 5.1),对垃圾回收的标记阶段的提升,所有位于old generations的对象都被标记为黑色,表示该对象为存活对象,对应三色标记法。

  • 可以直接申请到black page上;
  • 无需sweep;
  • 加速了incremental marking,因为通过black allocation的不会增加其工作量;

State of GC in V8

Scavenging

parallel scavenging

Major GC

concurrent Marking,concurrent Sweeping / parallel compaction + pointer updating

Idle-time GC

空闲时间gc

源码分析

源码位置在//v8/src/heap/目录下。
需要关注分析的文件:

1
2
3
4
5
6
7
8
9
10
# some basic utils
heap.cc -> heap definition
# minor gc
scavenger.cc -> minor gc scavenger
minor-mark-sweep.cc -> minor gc mark sweep
# major gc
mark-compact.cc -> major gc mark compact
# some techniques
evacuation-allocator.cc -> minor gc evacuation
incremental-marking.cc -> incremental-marking

GarbageCollector类定义//v8/src/heap/heap.cc

选择GarbageCollector函数Heap::SelectGarbageCollector//v8/src/heap/heap.cc

Mark-Compact

CollectGarbage函数(//v8/src/heap/mark-compact.cc),包含基本流程:
![[2025-07-14-V8内存回收机制/file-20250725163835844.png]]

Scavenger

CollectGarbage函数(//v8/src/heap/scavenger.cc),包含基本流程(swap semispace, iterate marking, promote, update):
该函数详见:Scavenger CollectGarbage

Mark-Sweep

CollectGarbage函数(//v8/src/heap/minor-mark-sweep.cc),包含基本流程:
![[2025-07-14-V8内存回收机制/file-20250726143701519.png]]

  • MarkLiveObjects函数:与Mark Compact的MarkLiveObject函数类似,但更关注于Young Generation对象。
  • Sweep函数:sweep+update

历史漏洞分析

Chrome V8漏洞(CVE-2021-37975)分析

详细分析见:CVE-2021-37075复现

Chrome V8漏洞(CVE-2022-1310)分析

参考文献