一直对这个概念不是很清楚,今天正好花时间了解了一下,搬过来也算自己加强记忆。学习自前端小智-原文地址的一篇文章。 (这也是本菜在掘金的第一篇文章)
不少有被面试官问到谈谈JS 垃圾回收机制,说实话,面试官会问这个问题,说明他最近看到一些关于 JS 垃圾回收机制的相关的文章,为了 B 格,就会顺带的问问。
垃圾回收
JavaScript 中的内存管理是自动执行的,而且是不可见的。我们创建基本类型、对象、函数……所有这些都需要内存。
当不再需要某样东西时会发生什么? JavaScript 引擎是如何发现并清理它?
可达性
JavaScript 中内存管理的主要概念是可达性。
简单地说,“可达性” 值就是那些以某种方式可访问或可用的值,它们被保证存储在内存中。
1. 有一组基本的固有可达值,由于显而易见的原因无法删除。例如:
1. 本地函数的局部变量和参数
2. 当前嵌套调用链上的其他函数的变量和参数
3. 全局变量
4. 还有一些其他的,内部的
这些值称为根
2. 如果引用或引用链可以从根访问任何其他值,则认为该值是可访问的。
例如,如果局部变量中有对象,并且该对象具有引用另一个对象的属性,则该对象被视为可达性, 它引用的那些也是可以访问的,详细的例子如下。
JavaScript 引擎中有一个后台进程称为垃圾回收器,它监视所有对象,并删除那些不可访问的对象。
一个简单的例子
// user 具有对象的引用
let user = {
name: "John"
};

如果 user 的值被覆盖,则引用丢失:
user = null;

两个引用
现在让我们假设我们将引用从 user 复制到 admin:
// user具有对象的引用
let user = {
name: "John"
};
let admin = user;

user = null;
该对象仍然可以通过 admin 全局变量访问,所以它在内存中。如果我们也覆盖admin,那么它可以被释放。
相互关联的对象
现在来看一个更复杂的例子, family 对象:
function marry (man, woman) {
woman.husban = man;
man.wife = woman;
return {
father: man,
mother: woman
}
}
let family = marry({
name: "John"
}, {
name: "Ann"
})
函数 marry 通过给两个对象彼此提供引用来“联姻”它们,并返回一个包含两个对象的新对象。
产生的内存结构:

现在让我们删除两个引用:
delete family.father;
delete family.mother.husband;

但是如果我们把这两个都删除,那么我们可以看到 John 不再有传入的引用:

垃圾回收之后:

无法访问的数据块
有可能整个相互连接的对象变得不可访问并从内存中删除。
源对象与上面的相同。然后:
family = null;
内存中的图片变成:

很明显,John和Ann仍然链接在一起,都有传入的引用。但这还不够。
“family”对象已经从根上断开了链接,不再有对它的引用,因此下面的整个块变得不可到达,并将被删除。
内部算法
基本的垃圾回收算法称为“标记-清除”,定期执行以下“垃圾回收”步骤:
1. 垃圾回收器获取根并“标记”(记住)它们。
2. 然后它访问并“标记”所有来自它们的引用。
3. 然后它访问标记的对象并标记它们的引用。所有被访问的对象都被记住,以便以后不再访问同一个对象两次
4. 以此类推,直到有未访问的引用(可以从根访问)为止。
5. 除标记的对象外,所有对象都被删除。
例如,对象结构如下:

第一步标记根

然后标记他们的引用

以及子孙代的引用:

现在进程中不能访问的对象被认为是不可访问的,将被删除:

一些优化:
1.分代回收——对象分为两组:“新对象”和“旧对象”。许多对象出现,完成它们的工作并迅速结 ,它们很快就会被清理干净。那些活得足够久的对象,会变“老”,并且很少接受检查。
2.增量回收——如果有很多对象,并且我们试图一次遍历并标记整个对象集,那么可能会花费一些时间,并在执行中会有一定的延迟。因此,引擎试图将垃圾回收分解为多个部分。然后,各个部分分别执行。这需要额外的标记来跟踪变化,这样有很多微小的延迟,而不是很大的延迟。
3.空闲时间收集——垃圾回收器只在 CPU 空闲时运行,以减少对执行的可能影响。
面试怎么回答
1)问什么是垃圾
一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。
2)如何检垃圾
JS中最常见的垃圾回收方式是标记清除。
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:
-
垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
-
去掉环境中的变量以及被环境中的变量引用的变量的标记。
-
再被加上标记的会被视为准备删除的变量。
-
垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
引用计数 方式
工作原理:跟踪记录每个值被引用的次数。
工作流程:
-
声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1。
-
同一个值又被赋值给另一个变量,这个引用类型值的引用次数加1.
-
当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1.
-
当引用次数变成0时,说明没办法访问这个值了。
-
当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存。
还想说出不同的算法可以参考这里。
更深入一些的讲解 newhtml.net/v8-garbage...
还有一种牛逼的答法就是说看我的博客,当然是要自己总结的博客。