javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JS垃圾回收与V8分代回收

JavaScript垃圾回收原理与V8分代回收示例详解

作者:Ziky学习记录

这篇文章主要介绍了JavaScript垃圾回收原理与V8分代回收的相关资料,在V8的老一代的垃圾回收机制中,采用的就是这种做法,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在我们写JavaScript的时候,我们很少手动释放内存。不像 C/C++ 需要使用free(), JS 引擎会帮我们自动回收内存。

但是,自动回收 != 不需要理解内存,很多前端的性能问题,页面卡顿,内存暴涨,其实都和垃圾回收机制有关。

因此理解垃圾回收机制也尤为重要。

什么是垃圾回收?

垃圾回收(Garbage Collection,简称 GC):

自动识别“无法再访问”的对象,并释放其占用的内存。

核心问题只有一个:如何判断一个对象“没用了”?

那么基于这个定义,没有被引用的对象,就是垃圾。比如

let obj = { name: "Tom" }
obj = null

obj = null 后:

内存模型基础

那么,JavaScript的内存中主要可以分为两个部分

比如:

let obj = { name: "Tom" }

垃圾回收核心原理

那么怎么知道对象是否是垃圾

标记清除算法

js引擎中有标记清楚算法用来判断是否是垃圾,整个过程就是从root出发,看看是否能够被访问到

Root包括:

那么整个算法过程就分为标记阶段与清楚阶段

  1. 标记阶段

假设我们目前有以下这些对象

Root
 ├── A
 │    └── B
 └── C

D

整个过程就是先从root出发,先找到了A与C,为A与C打上了“可达” 标记

继续向下遍历通过A找到了B,给B打上了可达标记,整个过程就是一次深度优先搜索(DFS)或广度优先搜索(BFS)

遍历结束后:

  1. 清除阶段

引擎会扫描整个堆内存。

对于每一个对象:

因此D将会被回收。

D(无标记)

我们知道js是单线程的,那么也就是在垃圾回收的时候,主线程暂停,会执行这个垃圾回收,那么如果频繁进入垃圾回收就会造成页面卡顿。

V8 的分代回收机制

在早期垃圾回收模型中,所有对象都被一视同仁地扫描和回收。但在实际运行中,V8 发现了一个非常重要的现象:

绝大多数对象的生命周期都非常短。

例如:

function render() {
  let temp = { x: 1, y: 2 }
  return temp.x
}

在这个函数中,temp 只在函数执行期间存在。函数执行结束后,它就不再被引用,很快就会成为垃圾对象。

但与此同时,也存在一些对象会贯穿整个应用生命周期:

const config = { ... }

这些对象几乎不会被销毁。

如果所有对象都使用同一种回收策略:

因此,V8 采用了 分代垃圾回收机制(Generational GC)

思想介绍

分代回收机制,将内存分成了两类

核心思想就是 新创建的对象大概率会很快死亡;存活较久的对象,大概率会继续存活。

新生代

首先,新创建的对象都先放在新生代,这个空间会比较小,大小通常比较小就几MB,并且这个空间回收频率快,回收速度高。

整个新生代又分成两个空间,

具体新生代使用的是 复制算法(Copying Collection),也称为 Scavenge 算法。

算法流程:

From Space:
[A][B][C][D]

To Space:
[空]

其中:

当垃圾回收执行时:

1️⃣ 停止 JS 执行

2️⃣ 从 Root 出发标记存活对象(这里与标记清除有区别,这里是标记复制,但是标记的流程是一样的)

3️⃣ 把“活着的对象”复制到 To Space

变成如下形式

To Space:
[A][C]

4️⃣ 清空整个 From Space

5️⃣ 交换两个空间角色

然后等待下一次执行

老生代

那么什么时候对象会进入老生代呢

进入老生代的条件是,:

老生代的特点就是

我们已经知道新生代的算法是复制算法,但是老生代使用的算法是

标记清楚我们已经介绍过了,那么这里我们介绍标记整理

在清除后,整个内存空间会存在很多空隙碎片,那么就需要进行整理:

[A][ ][B][ ][ ][C]
				|转换成下面的形式
[A][B][C][ ][ ][ ]

对象会被“挪动”到连续空间。

整个过程

我们可以看下面这个流程。

Root
  ↓
新对象 → 新生代(复制算法)
  ↓(存活多次)
晋升
  ↓
老生代(标记清楚 + 标记整理)

以上是整个分代回收基本流程,如果有兴趣可以再看看 写屏障等改进方法

内存泄漏是什么

内存泄漏就是该回收的对象一直被引用,导致无法回收。

具体有

意外的全局变量

functiontest() {
	a=10// 忘记 var / let
}

定时器未清除

setInterval(() => {},1000)

闭包持有大对象

function bibao() {
	let data = ...
	return function() {
		console.log(data)
	}
}
const test = bibao()
// 当 test不再被需要,仍然保持着对data引用,导致内存泄漏

如何减少或避免内存泄漏

为了避免内存问题,我们就需要针对上面可能出现的问题进行针对性解决

比如

总结

JavaScript 在 V8 中采用基于可达性的分代垃圾回收机制,通过复制算法优化短生命周期对象,通过标记清除与整理管理长生命周期对象

此外,如果有兴趣可以再了解 写屏障和记忆集解决跨代引用问题。

到此这篇关于JavaScript垃圾回收原理与V8分代回收的文章就介绍到这了,更多相关JS垃圾回收与V8分代回收内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文