ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

V8 中的垃圾回收机制

2021-09-22 18:04:31  阅读:137  来源: 互联网

标签:对象 JavaScript 回收 -- 垃圾 V8 内存


​ 在 JavaScript 中,原始数据类型是存储在栈区的,引用数据类型存储在堆区。在代码执行的过程中,一部分数据在被使用之后就不在被需要了,这些数据就变成了垃圾数据。如果不清除它们,内存消耗会越来越多。因此,需要对这些垃圾数据来做回收,释放有限的空间。

垃圾回收策略
  • 手动回收 -- C/C++ free()
    • 如果一段数据不再使用,又没有回收,就会造成内存泄漏
  • 自动垃圾回收策略 -- Python、Java、JavaScript 等
    • 产生的垃圾由垃圾回收器释放
JavaScript 中的垃圾回收机制
  • 调用栈

    • 调用栈中会有一个记录当前执行状态的指针(ESP:类似于数组实现的栈中记录栈顶的索引,弹栈时直接下移,入栈时如果新的位置之前有元素直接覆盖),当前代码块执行完毕后下移。
    • JavaScript 引擎通过向下移动 ESP 来销毁保存在栈中的执行上下文
    • 通过 JavaScript 中的垃圾回收器来回收堆中的垃圾数据

    • 代际假说 -- Java、JavaScript、Python 等等

      • 特点
        • 大部分对象在内存中的存在时间很短。即,很多对象在分配内存之后,很快就变得不可访问
        • 不死的对象,活得更久
    • V8 的实现

      • 将堆分为新生代和老生代两个区域

        • 新生代:存放生存周期短的对象 (容量 1 ~ 8 M)
        • 老生代:存放生存时间久的对象(容量比起新生代大得多)
      • V8 使用两个不同的垃圾回收器来实现高效的垃圾回收

        • 主垃圾回收器:负责老生代的垃圾回收
        • 副垃圾回收器:负责新生代的垃圾回收
      • 垃圾回收器的工作流程(垃圾回收器的工作流程相同,无论何种类型的垃圾回收器)

        1. 标记空间中的活动和非活动对象。
          • 活动对象:还在使用的对象
        2. 回收非活动对象占据的内存。 -- 在标记完成之后,统一清理内存中所有被标记为可回收的对象
        3. 内存整理
          • 频繁回收对象,内存中会出现大量的不连续空间 -- 内存碎片。如果内存中空间碎片较多,分配较大的连续空间的时候,可能会出现内存不足的情况。
      • 主垃圾回收器 -- 老生代

        • 除了新生区中晋升的对象,一般情况下,大的对象会直接分配到老生区。

        • 老生区中对象的特点

          1. 对象占用空间大
          2. 对象存活时间长
        • 由于对象较大,采取与新生区中相同的策略会导致回收的执行效率低下,同时也会浪费一半的空间。因此,主垃圾回收器采用 标记 -- 清除 来进行垃圾回收

        • 回收过程
          1. 从一组根元素开始,可以到达的元素是活动对象,不可到达的是垃圾数据( 调用栈中 ESP 下移之后,当前 ESP 上面的执行上下文 ) -- 标记
          2. 清除 -- 导致内存碎片
          3. 整理内存(标记 -- 整理算法:标记过程同标记--清除算法,之后让所有存活的对象向一端移动,然后直接清理掉端边界之外的内存

      • 副垃圾回收器 -- 新生代

        • 通常,大多数小的对象会被分配到新生区。新生区中垃圾回收比较频繁。
        • 使用 Scavenge(清除) 算法处理
          • 现将新生代对半划为两个区域:对象区域、空闲区域
          • 新加入的对象存放到对象区域,对象区域快被写满时,执行一次垃圾清理操作。
            • 首先标记对象区域中的垃圾
            • 在垃圾清理过程中,副垃圾回收器将对象区域中存活的对象复制到空闲区域,并且有序的排列起来 -- 内存整理,复制后空闲区域没有内存碎片
            • 复制操作完成后,对象区域和空闲区域角色翻转,完成垃圾对象的回收。翻转的操作可以使新生代中的两块区域无限重复的使用下去
          • 每次执行清理操作时,都需要将存活的对象从对象区域复制到空闲区域。由于复制需要时间成本,如果新生区的空间设置的过大,会导致每次清理的时间过久。为了执行效率,一般新生区的空间会设置的比较小
          • 新生区的空间较小,很容易被对象装满。JavaScript 引擎采取对象晋升策略来解决这个问题 -- 经过两次垃圾回收依然存活的对象会被移动到老生区中
    • 全停顿

      • JavaScript 运行在主线程之上,一旦执行垃圾回收算法,需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕之后恢复脚本的执行。 -- 这就是全停顿

      • V8 新生代的垃圾回收由于空间较小,存活对象较少,全停顿影响不大

      • 老生代中垃圾回收耗时较长,在这段时间中主线程不能做其他的事情。就会导致出现卡顿的现象

        • 为了解决这个问题,V8 将标记过程拆分为一个个子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直至标记完成。 -- 增量标记算法

        • 将一个完整的垃圾回收任务拆分成很多小的任务,小任务执行时间短,可以穿插在 JavaScript 任务中执行

        • 没有完美的解决方案,只能两害相权取其轻,两利相劝取其重

标签:对象,JavaScript,回收,--,垃圾,V8,内存
来源: https://www.cnblogs.com/honey-cat/p/15320989.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有