一文带你搞懂V8垃圾回收系统

 更新时间:2023年07月28日 08:22:40   作者:荣达  
在V8中,JavaScript的内存空间分为栈(Stack)和堆(Heap)两部分,垃圾回收的基本思路是:查找内存中的所有变量,看哪些已经不再需要,然后释放这些变量所占用的内存,本文就给大家梳理一下V8垃圾回收系统,需要的朋友可以参考下

内存中两种变量的存储

在V8中,JavaScript的内存空间分为栈(Stack)和堆(Heap)两部分。栈用于存储原始类型,如Number,String,Boolean,Null,Undefined,Symbol以及引用对象的内存地址,而堆用于存储引用类型的对象。

垃圾回收的基本思路是:查找内存中的所有变量,看哪些已经不再需要,然后释放这些变量所占用的内存。

所以V8中的垃圾回收可以分为栈回收堆回收

栈回收

在V8引擎中,函数调用的参数、返回地址和局部变量都存储在调用栈中。每当一个函数被调用时,都会创建一个新的栈帧,其中包含这些信息。而栈帧的回收则非常直接:一旦函数调用结束,其栈帧就会被立即移除。这种机制依赖于ESP(Extended Stack Pointer)指针,该指针始终指向栈的顶部,用于追踪哪些栈帧是活动的,哪些可以被安全回收。

简单理解:说白了就是某个作用域代码执行完毕后,自动回收作用域内所有的普通数据类型的垃圾。

堆回收

堆中变量存储的分类

V8引擎引入所谓的“代际假说”,即“大部分对象在内存中存在的时间很短”,所以把堆内存分为新生代老生代两部分,新生代即对象一开始创建时存放的区域,一旦对象满足了一定的条件(也就是V8认为其会长时间存在于内存),那么它就会被移动(“晋升”)至老生代。

所以针对堆内存中的垃圾回收,也分为两种算法来分别处理新老生代。

新生代垃圾回收

采用Scavenge算法,简单来说就是Copy,也叫新生代互换。

结合上图:新生代空间等分成From空间与To空间,新声明的变量存放到新生代的From空间,直到From空间满了,这时候需要进行GC的垃圾回收。GC会根据代码分析出哪些obj不是垃圾,然后把非垃圾的obj对象直接copy到To空间,最后把From空间中的垃圾进行删除即可,至此存放对象的To空间变成From空间,原本的From空间变成To空间,从而开启新一轮的对象存储。

之所以新生代采用这种回收算法,可以从时空复杂度进行分析,首先我们会发现新生代的空间在同一时刻只有一半被利用,把空间进行了折半,目的就是为了对象转移时直接进行copy操作(肯定耗时非常低),可以理解为空间换时间。

并且新生代的总空间相较于老生代比较小,所以折半空间不会损失太多的内存空间,可以接受。

老生代垃圾回收

标记清除

在V8引擎早期,采用的是Mark-Sweep算法(标记清除),如下图:

注:上面图示的GC根节点并不是指浏览器的window对象,但可以类比window对象,我们可以通过window.xxx.xxx.xx访问所有的变量,自然可以通过GC的根节点访问所有的内存对象,一旦GC断开了与某个对象的联系,那么这个对象就可以理解为垃圾。

标记清除算法的完整步骤:

  • 从GC的根节点开始进行“广度扫描”(专属术语)并把非垃圾进行标记
  • 清除未被标记的对象

标记整理

现阶段Mark-Compact算法(标记整理)已经成为主流,如下图:

标记整理算法步骤:

  • 先进行广度扫描并标记非垃圾变量
  • 整理非垃圾变量的位置,目的是获取更大的连续空间,如上图一到图二(整理的过程中会对一些垃圾进行覆盖,可以理解为先整理后清除的原因)
  • 垃圾清除

也就是相较于标记清除算法多了一步:在垃圾清除之前进行内存空间的整理

标记算法优化

如上我们所描述的标记,都是“全停顿标记”,也就是说js主线程切换至GC垃圾回收线程时,在GC线程中会把所有的应该标记的变量都进行标记,然后进行大规模的清除。

这种标记算法的劣势就是时间开销大,导致GC线程占用的时间长,阻塞js主线程的运行。所以后期标记算法也进行了优化,衍生出来增量标记与三色标记法,这里就不详细记录了,总之优化的核心思想就是部分标记,从而减少GC线程对js线程运行时间的抢占。

plus

当然更早期的IE浏览器还使用过引用计数的堆回收算法,存在着不能解决循环引用问题等痛点。这里不再赘述。

到此这篇关于一文搞懂V8垃圾回收系统的文章就介绍到这了,更多相关V8垃圾回收系统内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • element-ui el-table表格固定表头代码示例

    element-ui el-table表格固定表头代码示例

    Element-UI的el-table组件是一个非常强大的表格组件,它提供了多种常见的表格功能,如排序、筛选、分页、自定义列模板等,这篇文章主要给大家介绍了关于element-ui el-table表格固定表头的相关资料,需要的朋友可以参考下
    2024-05-05
  • Vue路由组件的缓存keep-alive和include属性的具体使用

    Vue路由组件的缓存keep-alive和include属性的具体使用

    :浏览器页面在进行切换时,原有的路由组件会被销毁,通过缓存可以保存被切换的路由组件,本文主要介绍了Vue路由组件的缓存keep-alive和include属性的具体使用,感兴趣的可以了解一下
    2023-11-11
  • vue页面params传值的坑及解决

    vue页面params传值的坑及解决

    这篇文章主要介绍了vue页面params传值的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • vue-router传参用法详解

    vue-router传参用法详解

    今天小编就为大家分享一篇关于vue-router传参用法详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • vue3父子组件传值 双向绑定及注意问题小结

    vue3父子组件传值 双向绑定及注意问题小结

    这篇文章主要介绍了Vue3中如何通过v-model实现父子组件的双向数据绑定及利用computed简化父子组件双向绑定,本文结合示例代码给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12
  • Vue.js进阶知识点总结

    Vue.js进阶知识点总结

    给大家分享了关于Vue.js想成为高手的5个总要知识点,需要的朋友可以学习下。
    2018-04-04
  • VUE配置history路由模式配置详细举例

    VUE配置history路由模式配置详细举例

    这篇文章主要给大家介绍了关于VUE配置history路由模式配置的相关资料,History模式是Vue路由的另一种模式,在History模式下URL中的路径不再使用#符号,而是直接使用正常的路径形式,需要的朋友可以参考下
    2023-12-12
  • vue组件间传值的方法你知道几种

    vue组件间传值的方法你知道几种

    这篇文章主要为大家详细介绍了vue组件间传值的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • 详解vue 自定义marquee无缝滚动组件

    详解vue 自定义marquee无缝滚动组件

    这篇文章主要介绍了vue自定义marquee无缝滚动组件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • 浅谈vue-props的default写不写有什么区别

    浅谈vue-props的default写不写有什么区别

    这篇文章主要介绍了浅谈vue-props的default写不写有什么区别,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08

最新评论