[译] 前端面试:谈谈 JS 垃圾回收机制

译者:前端小智javascript

原文:javascript.info/garbage-col…html

最近看到一些面试的回顾,很多有被面试官问到谈谈JS 垃圾回收机制,说实话,面试官会问这个问题,说明他最近看到一些关于 JS 垃圾回收机制的相关的文章,为了 B 格,就会顺带的问问。前端

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!java

最近看到一篇讲 JS 垃圾回收的国外文章,以为讲得明白,因此就翻译过来了,但愿对大家有所帮助。git

垃圾回收

JavaScript 中的内存管理是自动执行的,并且是不可见的。咱们建立基本类型、对象、函数……全部这些都须要内存。github

当再也不须要某样东西时会发生什么? JavaScript 引擎是如何发现并清理它?面试

可达性

JavaScript 中内存管理的主要概念是可达性。算法

简单地说,“可达性” 值就是那些以某种方式可访问或可用的值,它们被保证存储在内存中。函数

1. 有一组基本的固有可达值,因为显而易见的缘由没法删除。例如:学习

  • 本地函数的局部变量和参数

  • 当前嵌套调用链上的其余函数的变量和参数

  • 全局变量

  • 还有一些其余的,内部的

这些值称为根。

2. 若是引用或引用链能够从根访问任何其余值,则认为该值是可访问的。

例如,若是局部变量中有对象,而且该对象具备引用另外一个对象的属性,则该对象被视为可达性, 它引用的那些也是能够访问的,详细的例子以下。

JavaScript 引擎中有一个后台进程称为垃圾回收器,它监视全部对象,并删除那些不可访问的对象。

一个简单的例子

下面是最简单的例子:

// user 具备对象的引用
let user = {
  name: "John"
};
复制代码

图片描述

这里箭头表示一个对象引用。全局变量“user”引用对象 {name:“John”} (为了简洁起见,咱们将其命名为John)。John 的 “name” 属性存储一个基本类型,所以它被绘制在对象中。

若是 user 的值被覆盖,则引用丢失:

user = null;
复制代码

图片描述

如今 John 变成不可达的状态,没有办法访问它,没有对它的引用。垃圾回收器将丢弃 John 数据并释放内存。

两个引用

如今让咱们假设咱们将引用从 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 再也不有传入的引用:

图片描述

输出引用可有可无。只有传入的对象才能使对象可访问,所以,John 如今是不可访问的,并将从内存中删除全部不可访问的数据。

垃圾回收以后:

图片描述

没法访问的数据块

有可能整个相互链接的对象变得不可访问并从内存中删除。

源对象与上面的相同。而后:

family = null;
复制代码

内存中的图片变成:

图片描述

这个例子说明了可达性的概念是多么重要。

很明显,John和Ann仍然连接在一块儿,都有传入的引用。但这还不够。

“family”对象已经从根上断开了连接,再也不有对它的引用,所以下面的整个块变得不可到达,并将被删除。

内部算法

基本的垃圾回收算法称为**“标记-清除”**,按期执行如下“垃圾回收”步骤:

  • 垃圾回收器获取根并**“标记”**(记住)它们。
  • 而后它访问并“标记”全部来自它们的引用。
  • 而后它访问标记的对象并标记它们的引用。全部被访问的对象都被记住,以便之后再也不访问同一个对象两次。
  • 以此类推,直到有未访问的引用(能够从根访问)为止。
  • 除标记的对象外,全部对象都被删除。

例如,对象结构以下:

图片描述

咱们能够清楚地看到右边有一个“不可到达的块”。如今让咱们看看**“标记并清除”**垃圾回收器如何处理它。

第一步标记根

图片描述

而后标记他们的引用

图片描述

以及子孙代的引用:

图片描述

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

图片描述

这就是垃圾收集的工做原理。JavaScript引擎应用了许多优化,使其运行得更快,而且不影响执行。

一些优化:

  • 分代回收——对象分为两组:“新对象”和“旧对象”。许多对象出现,完成它们的工做并迅速结 ,它们很快就会被清理干净。那些活得足够久的对象,会变“老”,而且不多接受检查。

  • 增量回收——若是有不少对象,而且咱们试图一次遍历并标记整个对象集,那么可能会花费一些时间,并在执行中会有必定的延迟。所以,引擎试图将垃圾回收分解为多个部分。而后,各个部分分别执行。这须要额外的标记来跟踪变化,这样有不少微小的延迟,而不是很大的延迟。

  • 空闲时间收集——垃圾回收器只在 CPU 空闲时运行,以减小对执行的可能影响。

面试怎么回答

1)问什么是垃圾

通常来讲没有被引用的对象就是垃圾,就是要被清除, 有个例外若是几个对象引用造成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。

2)如何检垃圾

一种算法是标记 标记-清除 算法,还想说出不一样的算法能够参考这里

更深刻一些的讲解 newhtml.net/v8-garbage-…

还有一种牛逼的答法就是说看个人博客,固然是要本身总结的博客。

你的点赞是我持续分享好东西的动力,欢迎点赞!

交流

干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。

github.com/qq449245884…

我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,便可看到福利,你懂的。

相关文章
相关标签/搜索