从Js到V8引擎:垃圾回收

研究JS的垃圾回收机制,其实就是研究JS所运行时环境的内存管理机制。从原生JS的垃圾回收机制,再到V8引擎所使用的新的回收机制,每一种机制都值得拿出来探究,本文以图文的形式争取让大家理解。

垃圾回收常见的几个算法(机制)

  1. 引用计数
  2. 标记清除
  3. 标记清除整理
  4. Cheney算法:目前V8引擎新生代垃圾回收

我们来逐个讲解。


JS的垃圾回收机制

Js的垃圾回收机制主要分为两个:

  • 标记清除法
  • 引用计数法

前置概念:什么是循环引用:

循环引用非彼for循环。

循环引用指的是两个或多个对象之间相互引用对方,形成一个闭环的引用链。举例说,对象 A 引用了对象 B,而对象 B 也引用了对象 A,这样就形成了循环引用。

js 复制代码
// 定义一个构造函数 Node 表示节点
function Node() {
  this.neighbors = []
}
// 实例两个节点: left 和 right 
let left = new Node()
let right = new Node()
// 循环引用
left.neighbors.push(right)
right.neighbors.push(left)

// 现在 left 和 right 形成了循环引用
// 即使将它们设置为 null,也无法解除循环引用
left = null
right = null

标记清除法

这是Js垃圾回收机制中最常用的一种算法。

  1. 阶段一: 标记阶段

    1. 垃圾回收器会从跟对象(可以理解为全局对象和当前执行上下文的局部变量?)开始,遍历所有。
    2. 在遍历的过程中,会先给所有对象来标记一个状态,假设 1 所示在使用。
  2. 阶段二:清除标记

    1. 垃圾回收器会检查一边所有的对象(遍历),如果发现某个对象没被标记为 1 而是 0 (没被使用)
    2. 它会把所有标记为 0 的对象删除,释放内存。

这种方法的优点是能够有效处理循环引用的问题,因为只要对象被标记为 0 ,就会被回收,不管在不在循环体中。

引用计数

引用计数是一种较为简单的垃圾回收策略,因为简单,所以天然但存在一些缺陷,对于一些循环引用的对象难以处理。

  1. 阶段一: 引用计数机制

    1. 每个对象都有一个引用计数器属性(在原型链上),这个属性记录有多少个其他对象在引用它。
    2. 当一个新引用指向该对象时,引用计数器加一;当一个引用被删除时,引用计数器减一。
  2. 阶段二:回收阶段

    1. 对于哪些引用计数为 0 的对象,js就会判定他为未被引用,然后删除它。

无论是标记清除还是引用计数法,都是原生的JS的垃圾回收机制。他们天然存在一些缺陷。

  • 引用计数法: 对循环引用无法处理。
  • 标记清除法:存在暂停时间长、内存碎片以及非实时性问题。

上面既然说了 原生Js 对于垃圾回收机制存在缺陷 ,那么这些问题要怎么解决?其实强大的 V8引擎 就提出了新的方法和管理模式来对内存进行更准确地管理

V8 引擎中的垃圾回收机制

V8 引擎是 Google 开发的高性能 JavaScript 和 WebAssembly 引擎,广泛应用于 Chrome 浏览器和 Node.js 中。为了提升垃圾回收的效率和减少对应用程序性能的影响,V8 引擎对 JavaScript 的垃圾回收机制进行了多种优化。

关于V8有很多的地方可以研究,我们就来探究它对于垃圾回收的优化。

分代垃圾回收

V8引擎把内存划分为两块(Chenney算法):新生代、老生代。

  1. 新生代

    1. 存放一些生命周期比较短的对象。
    2. 用了特殊的算法:通过复制收集机制,将还在存活的对象从一个空间复制到另外一个空间,没有被复制的对象(未使用的)就会被回收。
    3. 这个特殊的算法执行时间短,成本低,新生代对象存活时间比较短,回收频率高。
  2. 老生代

    1. 存放一些生命周期较长的对象。
    2. 使用标记-清除和标记-压缩算法。
    3. 标记-清除:遍历并标记活跃对象,然后清除未标记的对象。
    4. 标记-压缩:在清除阶段后进行内存整理,将存活对象压缩到内存的一端,以减少内存碎片。

很抽象,较难理解。。。

  1. 初始时,新生代空间存在1,2,3三个对象,占用空间大小我们用矩形面积来表示。这个时候新增变量 4,但是原先内存空间因为不连续,导致4放不下。
  1. 于是,复制算法作用下,将新生代和即将加入的 4 复制放入到 老生代空间,并用进行了重排。
  1. 复制收集完成后,(交换空间)将老生代和新生代互换,等下下次对象的变化。

以上只是新生代和老生代转变重排的示例,在接下来的循环中,新老生代中复制收集算法、标记清除算法同时生效。将不活跃的老生代清除掉,来释放空间。

V8在垃圾回收时用到的机制:

  1. 增量标记:将标记阶段分成多个小步骤,逐步完成标记过程。
  2. 并发标记: 在后台线程中执行标记操作,不阻塞主线程。
  3. 延迟清理: 在标记阶段完成后,并不立即进行全部清理操作。而是将清理操作分散到后续的内存分配过程中,这样可以平摊清理成本,避免一次性清理带来的性能开销。
  4. 并行 清理: 在多个线程中并行执行清理操作。
  5. 内存 压缩: 将存活对象移动到内存的另一端,释放出连续的大块内存空间。

Cheney算法

上面说到,V8把内存划分为两块,其实这其中用到了Chenney算法:一种高效的垃圾回收算法,属于复制收集(Copying Collection)技术的一种。

Cheney算法将内存分成两个半空间,分别称为"从空间"(From-space)和"到空间"(To-space)。

浅谈Cheney算法原理

  1. 初始化: 所有对象最初都分配在"从空间"中。

  2. 复制阶段:从根对象开始,遍历所有可达对象,将它们从"从空间"复制到"到空间"。

  3. 更新阶段: 在复制过程中,更新所有对象的引用,以确保它们指向"到空间"中的新对象。

  4. 交换空间: 完成复制后,交换"从空间"和"到空间"的角色,原来的"到空间"变为新的"从空间",所有存活对象现在都在这个空间中,原来的"从空间"则可以完全清空。

相关推荐
superman超哥19 分钟前
04 深入 Oracle 并发世界:MVCC、锁、闩锁、事务隔离与并发性能优化的探索
数据库·oracle·性能优化·dba
熊的猫35 分钟前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
瑶琴AI前端1 小时前
uniapp组件实现省市区三级联动选择
java·前端·uni-app
会发光的猪。1 小时前
如何在vscode中安装git详细新手教程
前端·ide·git·vscode
我要洋人死2 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人2 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人2 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR3 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香3 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596933 小时前
前端预览word、excel、ppt
前端·word·excel