欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > V8 GC垃圾回收

V8 GC垃圾回收

2025/2/11 16:29:17 来源:https://blog.csdn.net/weixin_44275941/article/details/145512875  浏览:    关键词:V8 GC垃圾回收

V8 的垃圾回收机制是基于 使用 分代回收 策略来管理堆内存,将堆内存划分为 新生代(Young Generation)老生代(Old Generation)。这种分代回收方法基于以下假设:大多数对象都很快变得不可达(短生命周期),因此新生代对象的回收频率较高,而老生代对象存活时间较长,回收频率较低。

1. 分代回收(Generational GC)

V8 引擎采用了 分代回收 的策略,将堆内存分为 新生代老生代。新生代存储生命周期较短的对象,老生代存储生命周期较长的对象。分代回收通过针对不同类型对象使用不同的回收算法,提高了垃圾回收的效率。

  • 新生代:存放短生命周期的对象。
  • 老生代:存放长期存活的对象,通常是存活时间较长的对象或晋升过来的对象。

2. 新生代回收机制

V8 对新生代采用 复制算法(Copying Algorithm) 进行回收,具体过程如下:

  • 新生代被划分为 Eden 区两个 Survivor 区(S0 和 S1)。
    • Eden 区:新创建的对象会首先分配在这里。
    • Survivor 区:Eden 区和两个 Survivor 区之间会有一个用于存活对象的交换区。存活的(可达性分析,通过根对象(如全局对象、活动的栈等)和对象之间的引用关系来确定哪些对象是存活的,哪些对象是垃圾)对象会被复制到另一个 Survivor 区,长期存活的对象会被晋升到老生代。

当垃圾回收发生时,V8 会执行以下步骤:

  • 清除 Eden 区 中不可达的对象,将存活的对象复制到一个 Survivor 区
  • 如果一个 Survivor 区满了,存活的对象会在两个 Survivor 区之间交换。
  • 存活多次的对象会被晋升到 老生代

这种机制对于新生代对象的回收非常高效,因为大多数新生代对象都会迅速变得不可达,因此复制算法非常适合新生代的快速清理。

3. 老生代回收机制

老生代采用了更复杂的回收机制,通常是 标记-清除(Mark-and-Sweep)进行垃圾回收

  • 标记阶段:首先通过标记根对象开始扫描,标记所有可达的对象。
  • 清除阶段:清除未被标记的对象,即不可达的对象。

但此方式会有很多问题:

  • 性能开销:标记阶段,垃圾回收器需要遍历堆中所有的对象,递归标记出存活对象,这可能会涉及大量内存操作
  • 碎片化:内存是断续的,内存使用效率降低,使得在之后的内存分配中无法找到足够大的连续内存块,从而增加内存分配的成本
  • 卡顿:在垃圾回收过程中,程序的执行会暂停一段时间,直到垃圾回收完成

为处理上述问题,引申多种优化手段:

3.1 标记-压缩(Mark-and-Compact)
  • 在标记清除之后,V8 会进行内存压缩,移动存活对象,将它们压缩到堆的一个区域,从而消除内存碎片。这样可以提高堆内存的利用率。
3.2 增量标记(Incremental Marking)

老生代回收较为复杂,通常会导致较长的停顿时间。为了减少这种停顿,V8 引擎还使用了 增量标记(Incremental Marking)和 并行回收(Parallel GC)来分担回收任务,逐步清理老生代对象,而不是在一个长时间的回收周期内进行完整清理。

增量标记是 V8 引擎的一项优化技术,它的目的是减少垃圾回收期间的停顿时间。在传统的标记-清除算法中,标记阶段可能会导致一次较长时间的停顿,而增量标记通过将标记过程拆分成多个小的阶段,逐步标记堆中的对象,逐步清理老生代对象,而不是在一个长时间的回收周期内进行完整清理。

增量标记通常会在应用程序空闲时进行,将垃圾回收的标记过程分散到多个时间点执行。这样就能减少单次垃圾回收时的停顿时间,提高响应性。

3.2 并行垃圾回收(Parallel GC)

并行垃圾回收是 V8 引擎的一项重要优化,它允许垃圾回收过程的多个阶段在多核 CPU 上并行执行,从而大大减少垃圾回收的总停顿时间。

  • 在现代的 V8 引擎中,回收过程中的一些操作(如标记和清除)可以并行执行,从而加快回收过程,减少对主线程的影响。
  • 这种并行垃圾回收方式特别适用于多核处理器,可以有效利用硬件资源,提高垃圾回收的效率。

4. 其它

  • 最新优化:
    • V8 引擎还不断进行优化,通过自动调整垃圾回收策略和频率来应对不同类型的应用程序。例如,对于响应式的 web 应用,V8 会减少垃圾回收的频率,而对于内存密集型的应用,可能会增加回收的频率。此外,V8 还会根据堆的大小、对象的存活情况等动态调整回收策略。
    • 增加了并行化的标记和清理阶段,即使是老生代的回收过程也可以并行执行,减少了长时间停顿的风险。
    • 提高了增量标记的效果,使得标记过程在用户操作的空隙中进行,进一步减少了垃圾回收时的停顿。
    • 内存压缩优化,V8 引擎不断优化标记-压缩阶段,使得回收的内存空间更加紧凑,减少了内存碎片,提高了性能。
  • 引用计数问题:
    • 循环引用时,无法被回收,现V8引擎基本上不再使用该方式进行回收
function createCircularReference() {let objA = {};let objB = {};objA.b = objB;objB.a = objA;return objA;
}let circularObj = createCircularReference();
// circularObj.a.b = circularObj.b.a = objA
    • 每次创建、销毁或更新对象引用时,都需要对引用计数进行更新,这会带来额外的计算开销,尤其是对于频繁创建和销毁对象的程序
  • 标记清除问题(使用标记压缩进行优化):
    • 由于标记清除需要在堆内存中扫描所有对象并标记它们,尤其是老生代(长期存活对象)需要花费较长时间。因此,垃圾回收可能导致长时间的停顿,影响程序的性能
    • 标记清除只会删除不可达对象,并不会整理堆内存。由于没有对堆内存进行压缩,内存中可能会留下大量碎片,导致内存利用效率低下

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com