最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 高性能 JavaScript 引擎 V8 - 垃圾回收

    正文概述 掘金(微医大前端技术)   2021-03-02   671

    JavaScript 是一门具有自动垃圾收集机制的编程语言,由执行环境负责在代码执行时管理内存,当运行 javascript 时,你需要一个引擎来处理它,无论是在浏览器还是在 node.js 环境中, V8是你的选择之一,也一定是你的不二选择。下面我们就一起来看看V8是怎么进行高效的垃圾回收的吧。

    垃圾数据是怎么产生的

    • 首先,我们来看看垃圾数据是怎么产生的?看如下代码:
     let test = new Object()
     test.a = new Array(10)
    

    当 JavaScript 执行这段代码的时候,会先在全局作用域中添加一个 test 变量,并在堆中创建了一个空对象,将该对象的地址指向了 test。

    随后又创建一个大小为 10 的数组,并将属性地址指向了 test.a。此时的内存布局图如下所示: 高性能 JavaScript 引擎 V8 - 垃圾回收

    如果此时,我将另外一个对象赋给了 a 属性,代码如下所示:

    test.a = new Object()

    那么此时的内存布局如下所示: 高性能 JavaScript 引擎 V8 - 垃圾回收

    我们可以看到,a 属性之前是指向堆中数组对象的,现在已经指向了另外一个空对象,那么此时堆中的数组对象就成为了垃圾数据,因为我们无法从一个根对象遍历到这个 Array 对象,那么此时数组对象就成了垃圾数据。

    垃圾回收算法

    垃圾回收的实现简单分为以下三个步骤:

    第一步:可访问性

    从 GC Roots 对象出发,遍历 GC Root 中的所有对象:

    • 可访问对象:通过 GC Root 遍历到的对象,我们就认为该对象是可访问的(reachable),那么必须保证这些对象应该在内存中保留。
    • 不可访问对象:通过 GC Roots 没有遍历到的对象,则是不可访问的(unreachable),并会对其坐上标记,那么这些不可访问的对象就可能被回收。

    第二步:回收不可访问对象所占据的内存

    • 其实就是在所有的标记完成之后,统一清理内存中所有被标记为可回收的对象。

    第三步:内存整理

    • 频繁回收对象后,内存中就会存在大量不连续空间,称为内存碎片。当出现了大量的内存碎片之后,如果需要分配较大的连续内存时,就会出现内存不足的情况,所以最后一步需要整理这些内存碎片。但这步不是必须的,比如接下来我们要介绍的副垃圾回收器就不会产生内存碎片。

    以上就是大致的垃圾回收的流程。目前 V8 采用了两个垃圾回收器,主垃圾回收器和副垃圾回收器,下面我们再具体来看看两个回收器是怎么回收垃圾的。

    副垃圾回收器和主垃圾回收器

    • 在 V8 中,会把堆分为新生代(新生代通常只支持 1~8M 的容量)和老生代(容量大)两个区域,新生代中存放的是生存时间短的对象,老生代中存放生存时间久的对象。

    副垃圾回收器

    • 负责新生代的垃圾回收,大多数小的对象都会被分配到新生代,垃圾回收比较频繁。
    • 新生代中的垃圾数据用 Scavenge 算法来处理。分为两个区域:对象区域 ,空闲区域。如下图所示:

    高性能 JavaScript 引擎 V8 - 垃圾回收

    • 垃圾回收过程:
      • 1.垃圾标记和清理:首先要对对象区域中的垃圾做标记;标记完成之后,就进入垃圾清理阶段,如下图:

    高性能 JavaScript 引擎 V8 - 垃圾回收

    > 图中可以看到,副垃圾回收器会把这些我们仍然在用的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来,在复制过程,相当于完成了内存整理操作,复制后空闲区域就没有内存碎片了。
    
    • 2.角色翻转:完成复制后,进行角色翻转。把原来的对象区变成空闲区,把原来的空闲区变成对象区,如下图:

    高性能 JavaScript 引擎 V8 - 垃圾回收

    主垃圾回收器

    • 负责老生代中的垃圾回收,大多数占用空间大、存活时间长的对象都会被分配到老生代里。

    • 老生代中的垃圾数据用——标记 - 清除算法进行垃圾回收,因为老生代中的对象通常比较大,复制大对象非常耗时,会导致回收执行效率不高,所以采用标记清除法。

    • 垃圾回收过程:

      • 1.标记:标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。

      • 2.清除:它和副垃圾回收器的垃圾清除过程完全不同,主垃圾回收器会直接将标记为垃圾的数据清理掉,如下图:

    高性能 JavaScript 引擎 V8 - 垃圾回收

    • 3.整理:从上图可以看到,清除后会产生大量不连续的内存碎片,过多的碎片会导致大对象无法分配到足够的连续内存,于是需要引进另一种算法——标记 - 整理,整理过程如下图:

    高性能 JavaScript 引擎 V8 - 垃圾回收

    优化垃圾回收器

    • 由于 JavaScript 是运行在主线程之上的,在垃圾回收时会阻塞 JavaScript 脚本的执行,会造成页面卡顿等问题,使得用户体验不佳。

    • 为了解决上述问题,V8 团队推出了并行、并发和增量等垃圾回收技术,这些技术主要是从两方面来解决垃圾回收效率问题的:

      • 1.将一个完整的垃圾回收的任务拆分成多个小的任务,解决单个垃圾回收时间长的问题。
      • 2.将标记对象、移动对象等任务转移到后台线程进行,减少主阻塞线程的时间。

    接下来我们一起来看下具体这几种技术时怎么优化的。

    并行回收

    • 如果只有一个主线程进行垃圾回收,会造成停顿时间过长。所以 V8 团队推出主线程在执行垃圾回收的任务时,引入多个辅助线程来并行处理,这样就会加速垃圾回收的执行速度,如下图:

    高性能 JavaScript 引擎 V8 - 垃圾回收

    • 副垃圾回收器所采用的就是并行策略,它在执行垃圾回收的过程中,启动了多个线程来负责新生代中的垃圾清理操作,这些线程同时将对象空间中的数据移动到空闲区域。由于数据的地址发生了改变,所以还需要同步更新引用这些对象的指针

    增量回收

    • 并行回收虽然能增加垃圾回收效率,但是还是一种阻塞的方式进行垃圾回收,试想下引老生代中存在一个很大的对象,还是会造成一个长时间暂停。

    • 增量回收采用将标记工作把垃圾回收工作分解为更小的块,每次只进行小部分垃圾回收,减少主线程阻塞时间,如下图:

    高性能 JavaScript 引擎 V8 - 垃圾回收

    并发回收

    • 虽然增量回收已经能大大降低我们主线程阻塞的时间,但是所有的标记和清除还是在主线程上。那有没有办法可以在不阻塞主线程情况下执行呢?也由此 V8 推出了并发回收。

    • 并发回收,是指主线程在执行 JavaScript 的过程中,辅助线程能够在后台完成执行垃圾回收的操作,如下图:

    高性能 JavaScript 引擎 V8 - 垃圾回收

    在实际的应用中,这三种回收机制通常是融合在一起用的。

    参考资料

    • 图解 Google V8

    起源地下载网 » 高性能 JavaScript 引擎 V8 - 垃圾回收

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元