V8引擎中的垃圾回收机制如何工作?

V8引擎中的垃圾回收机制主要通过分代回收和增量标记清除算法来管理内存。以下是其工作原理的详细说明:

V8 的垃圾回收机制基于以下核心设计原则:

  1. 分代假设:大多数对象的生命周期很短,只有少数对象会存活较长时间;

  2. 减少主线程停顿:通过增量标记、并发标记和惰性清理等技术,尽量减少垃圾回收对 JavaScript 主线程的影响;

  3. 高效内存管理:针对不同代的内存区域,采用不同的回收算法,以平衡性能和内存利用率;

V8 的内存堆分为两个主要区域:新生代和老生代。下面分别详细介绍它们的回收机制。

1. 新生代垃圾回收

新生代用于存放生命周期短的对象。V8 对新生代采用 Scavenge 算法,这是一种基于复制的垃圾回收算法。

1.1. Scavenge 算法的工作原理

  1. 内存划分

新生代内存被划分为两个等大的半空间:From 空间和To 空间。新对象首先被分配到 From 空间。

  1. 对象分配

当 From 空间被填满时,V8 会触发一次新生代垃圾回收。

  1. 垃圾回收过程

(1). 标记存活对象:从根对象(如全局对象、当前函数调用栈等)出发,遍历对象图,标记所有存活的对象;

(2). 复制存活对象:将标记的存活对象从 From 空间复制到 To 空间;

(3). 清空 From 空间:复制完成后,清空 From 空间中的所有对象;

(4). 角色交换:From 空间和 To 空间的角色互换,原来的 To 空间变为新的 From 空间;

  1. 对象晋升

(1). 如果一个对象在多次新生代垃圾回收后仍然存活,它会被晋升到老生代;

(2). 如果 To 空间已经使用了超过 25%,则本次复制的对象会直接晋升到老生代;

1.2. Scavenge 算法的优点

  1. 高效:只复制存活对象,适合生命周期短的对象;

  2. 快速:新生代内存较小,回收速度快;

1.3. Scavenge 算法的缺点

  1. 内存浪费:始终有一半的内存空间(To 空间)处于闲置状态;

  2. 不适合大对象:复制大对象成本较高;

2. 老生代垃圾回收

老生代用于存放生命周期较长的对象,或者从新生代晋升的对象。V8 对老生代采用 标记清除和 标记整理算法。

2.1. 标记清除算法

  1. 标记阶段

(1). 从根对象出发,遍历整个对象图,标记所有存活的对象;

(2). 使用三色标记法(白色、灰色、黑色)来跟踪对象的标记状态;

  1. 清除阶段

(1). 遍历整个内存区域,清除所有未被标记的对象,即垃圾对象;

(2). 清除后的内存会留下碎片;

2.2. 标记整理算法

  1. 标记阶段

(1). 与标记清除算法相同,标记所有存活的对象;

  1. 整理阶段

(1). 将所有存活对象向内存的一端移动,使它们占据连续的内存空间;

(2). 清理剩余的内存空间,消除内存碎片;

2.3. 老生代算法的选择

  1. 标记清除:速度快,但会产生内存碎片;

  2. 标记整理:速度较慢,但可以消除内存碎片,适合内存紧张的场景;

  3. V8 会根据内存使用情况和碎片程度动态选择算法;

3. 增量标记

为了减少垃圾回收对主线程的阻塞,V8 引入了增量标记技术。

3.1. 增量标记的工作原理

  1. 分阶段标记

(1). 将标记过程分解为多个小步骤,与 JavaScript 主线程交替执行;

(2). 每次只标记一部分对象,避免长时间阻塞主线程;

  1. 写屏障

(1). 在增量标记期间,V8 使用写屏障技术来跟踪对象引用的变化;

(2). 当 JavaScript 代码修改对象引用时,写屏障会将被修改的对象标记为"脏对象",确保标记的准确性;

3.2. 增量标记的优点

  1. 减少停顿时间:将长时间的标记过程分散到多个小步骤中,避免主线程长时间阻塞;

  2. 提高响应速度:使应用程序更加流畅;

4. 惰性清理

在标记阶段完成后,V8 并不立即执行清除操作,而是采用惰性清理策略。

4.1. 惰性清理的工作原理

  1. 延迟清除

(1). 标记完成后,V8 不会立即清除未标记的对象,而是将清除操作推迟到后续的空闲时间或内存不足时执行;

  1. 按需清理

(1). 当需要分配新内存时,V8 会优先清理未标记的对象,以腾出可用内存;

4.2. 惰性清理的优点

  1. 减少主线程阻塞:将清除操作分散到多个时间点执行,避免一次性清理带来的性能开销;

  2. 提高效率:只在需要时执行清理操作,减少不必要的开销;

5. 并发标记和清理

为了进一步减少垃圾回收对主线程的影响,V8 引入了并发标记和清理技术。

5.1. 并发标记

  1. 在后台线程中执行标记操作,与 JavaScript 主线程并行运行.

  2. 主线程可以继续执行 JavaScript 代码,而不会因为标记操作而阻塞;

5.2. 并发清理

  1. 在后台线程中执行清除操作,减少主线程的负担;

5.3. 并发标记和清理的优点

  1. 最大化利用多核 CPU:利用多核 CPU 的并行计算能力,提高垃圾回收效率;

  2. 减少主线程停顿:使应用程序更加流畅;

6. 其他优化技术

6.1. 并行 Scavenge

  1. 在新生代垃圾回收中,V8 使用多线程并行执行复制操作,加快回收速度;

6.2. 空闲时间垃圾回收

  1. 在浏览器空闲时,如页面没有用户交互时,V8 会主动执行垃圾回收,减少对用户操作的干扰;

6.3. 内存限制

  1. V8 会根据可用内存动态调整垃圾回收的频率和策略,避免内存占用过高;

7. 总结

V8 的垃圾回收机制通过分代回收、增量标记、惰性清理和并发标记清理等技术,实现了高效的内存管理和低延迟的垃圾回收。

其核心思想是:

  1. 分而治之:将内存分为新生代和老生代,采用不同的回收策略;

  2. 减少停顿:通过增量标记和并发回收,尽量减少对主线程的影响;

  3. 动态优化:根据内存使用情况和应用需求,动态调整垃圾回收策略;

这些设计使得 V8 能够在高性能和低延迟之间取得平衡,为现代 JavaScript 应用提供强大的内存管理支持。

相关推荐
你挚爱的强哥1 小时前
【pdf】自定义组件:预览指定地址的PDF文件
开发语言·前端·javascript
linux-hzh1 小时前
day07
前端·javascript·css
新中地GIS开发老师2 小时前
新中地三维GIS开发智慧城市效果和应用场景
javascript·arcgis·智慧城市·gis开发·webgis·地理信息科学
Mintopia2 小时前
当像素跳起光影圆舞曲:用 JavaScript 解锁实时全局光照的魔法
前端·javascript·计算机图形学
温正实2 小时前
spring boot 实现接入 deepseek gpt接口 流式输出
javascript·spring boot·gpt
李q华2 小时前
react与vue的渲染原理
javascript·vue.js·react.js
aiguangyuan2 小时前
React 核心概念与生态系统
react·前端开发
爱看书的小沐2 小时前
【小沐杂货铺】基于Three.JS绘制太阳系Solar System(GIS 、WebGL、vue、react,提供全部源代码)第2期
javascript·vue.js·gis·webgl·three.js·地球·earth
aiguangyuan2 小时前
React 18 生命周期详解与并发模式下的变化
react·前端开发
前端设计诗2 小时前
Cursor Figma MCP 完整使用教程
前端·javascript·cursor