V8 的垃圾回收机制

为何需要垃圾回收

默认情况下,V8引擎在64位系统下最多只能使用约1.4GB的内存,在32位系统下最多只能使用约0.7GB的内存,如果不进行垃圾回收那一个程序运行不了多久就会把内存打爆了

垃圾回收策略

V8的垃圾回收策略主要是基于分代式垃圾回收机制,其根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同的分代采用不同的垃圾回收算法,这里主要讲新生代和老生代

新生代

新生代主要用于存放存活时间较短的对象。新生代内存是由两个半空间(From、To空间)构成的,内存最大值在64位系统和32位系统上分别为32MB和16MB,在新生代的垃圾回收过程中主要采用了Scavenge算法。
Scavenge算法是一种典型的牺牲空间换取时间的算法,在新生代内存中,大部分对象的生命周期较短,在时间效率上表现可观。

  1. 假设我们在From空间中分配了三个对象A、B、C
  2. 当程序主线程任务第一次执行完毕后进入垃圾回收时,发现对象A已经没有其他引用,则表示可以对其进行回收
  3. 对象B和对象C此时依旧处于活跃状态,因此会被复制到To空间中进行保存
  4. 接下来将From空间全部清除
  5. From空间和To空间完成一次角色互换

对象晋升

当一个对象在经过多次复制之后依旧存活,那么它会被认为是一个生命周期较长的对象,在下一次进行垃圾回收时,该对象会被直接转移到老生代中,这种对象从新生代转移到老生代的过程我们称之为晋升。

对象晋升的条件主要有以下两个:

  1. 对象是否经历过一次Scavenge算法
  2. To空间的内存占比是否已经超过25%

之所以有25%的内存限制是因为如果内存使用过高,则会影响后续对象的分配(就是刚复制过去没写多少内容From空间又满了)。

老生代

在老生代中,因为管理着大量的存活对象,如果依旧使用Scavenge算法的话,会浪费一半的内存,因此已经不再使用Scavenge算法,而是采用新的算法Mark-Sweep(标记清除)和Mark-Compact(标记整理)来进行管理。

Mark-Sweep(标记清除)

Mark-Sweep(标记清除)分为标记和清除两个阶段,在标记阶段会遍历堆中的所有对象,然后标记活着的对象,在清除阶段中,会将死亡的对象进行清除。Mark-Sweep算法主要是通过判断某个对象是否可以被访问到,从而知道该对象是否应该被回收,具体步骤如下:

  1. 垃圾回收器会在内部构建一个根列表,用于从根节点出发去寻找那些可以被访问到的变量。比如在JavaScript中,window全局对象可以看成一个根节点。
  2. 然后,垃圾回收器从所有根节点出发,遍历其可以访问到的子节点,并将其标记为活动的,根节点不能到达的地方即为非活动的,将会被视为垃圾。
  3. 最后,垃圾回收器将会释放所有非活动的内存块,并将其归还给操作系统。

Mark-Compact(标记整理)

Mark-Sweep算法存在一个问题,就是在经历过一次标记清除后,内存空间可能会出现不连续的状态,导致后面如果需要分配一个大对象而空闲内存不足以分配。
为了解决这种内存碎片的问题,Mark-Compact(标记整理)算法被提了出来,该算法主要就是用来解决内存的碎片化问题的,回收过程中将死亡对象清除后,在整理的过程中,会将活动的对象往堆内存的一端进行移动,移动完成后再清理掉边界外的全部内存。

  1. 假设在老生代中有A、B、C、D四个对象

  2. 在垃圾回收的标记阶段,将对象A和对象C标记为活动的

  3. 在垃圾回收的整理阶段,将活动的对象往堆内存的一端移动

  4. 在垃圾回收的清除阶段,将活动对象左侧的内存全部回收

    由于JS的单线程机制,垃圾回收的过程会阻碍主线程同步任务的执行,待执行完垃圾回收后才会再次恢复执行主任务的逻辑,这种行为被称为全停顿(stop-the-world)
    为了减少垃圾回收带来的停顿时间,V8引擎又引入了Incremental Marking(增量标记)的概念,即将原本需要一次性遍历堆内存的操作改为增量标记的方式,先标记堆内存中的一部分对象,然后暂停,将执行权重新交给JS主线程,待主线程任务执行完毕后再从原来暂停标记的地方继续标记,直到标记完整个堆内存。这个理念其实有点像React框架中的Fiber架构

如何避免内存泄露

  1. 尽可能少地创建全局变量
  2. 手动清除定时器
  3. 慎用闭包
  4. 使用 WeakMap/WeakSet

参考

Author: Wang Chunlin
Link: https://github.com/onechunlin/blog/2024/04/29/V8 的垃圾回收机制/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.