javascript垃圾回收机制

451 阅读4分钟

前言

前文我们提到过,活动对象AO在执行上下文出栈后会被垃圾回收。那什么是垃圾回收呢?

正文

今天我们就一起了解下javascript垃圾回收机制,简称GC

常见的两种垃圾回收策略:

  • 标记清除法
  • 引用计数法(存在循环引用的bug)

引用计数法

变量被引用计数+1,引用断开计数-1。当一个变量的引用计数为0时,清空内存。但是如果遇到循环引用的问题时就会存在bug,导致部分内存空间一直无法清空。导致内存泄漏

标记清除法

javascript垃圾回收机制就是使用标记清除法。 标记清除法分为两个步骤:

  • 标记:函数内声明一个变量,就将此变量标记为进入环境,当函数执行完成后。将变量标记为离开环境
  • 清除: 清空内存空间 但是GC是引擎会停止响应其他操作,这样就非常影响性能。所以需要对GC算法进行优化。

V8引擎GC -- 分代回收

分代回收将内存地址分为两个部分:

  • 新生代: 存放生存周期较短的变量
  • 老生代: 存放生存周期较大的变量

新生代

在新生代内存空间中的GC算法使用的是Scavenge。实现是采用Cheney算法。

他将内存空间一分为二。一块处于使用状态,一块处于闲置状态。处于使用状态的称为From空间,处于闲置状态的称为To空间。是一种用空间换时间的算法。

具体流程我以以下图讲解:

首先内存中有ABC三个变量,都存放在From空间中

接着B变量不在被引用,此时执行GC,会将AC变量从From空间中移动到To空间中

然后将From空间清空,最后将To空间和From空间互换

而当一个变量在两次从From空间移动到To空间时。他就会被提升到老生代内存空间中,这种过程叫做晋升。

晋升

当一个变量经过多次复制仍然存活时,它就会被认为是生命周期较长的对象。这种较长生命周期的对象随后会被移动到老生代中,采用新的算法进行管理。

产生晋升有两个条件:

  • 当一个变量在两次从From空间移动到To空间时
  • 当一个变量从From空间移动到To空间时,To空间的使用量已经超过25%时

老生代

在老生代中的变量生命周期都比较长。内存也比较大,此时就用到了我们之前提到的标记清除法。V8引擎采用Mark-Sweep和Mark-Compact相结合方式。

Mark-Sweep

就是标记清除法。遍历整个内存空间。标记活跃的变量,清空失活的变量。 以下图讲解具体流程:

首先如果老生代中有5个变量ABCDE 接着,BD两个变量失去引用,开始执行GC,遍历内存空间标记ACE为活跃变量 最后清空BD内存空间

从图片我们就能发现。这种方法会导致内存中有大量的碎片空间,如果此时要分配一个大内存,就有可能无法完成分配。为了解决此问题。Mark-Compact应运而生。

Mark-Compact

以下图讲解具体流程: 首先如果老生代中有5个变量ABCDE 接着,BD两个变量失去引用,开始执行GC,遍历内存空间标记ACE为活跃变量 然后GC会将ACE移动到内存地址的一头。并标记内存地址的结束位置。 最后,将结束位置之后的内存地址全部清空

由于Mark-Conpact需要移动对象,所以它的执行速度不会很快,所以V8主要使用Mark-Sweep,在空间不足以对从新生代中晋升过来的对象进行分配时,才使用Mark-Compact。

关注公众号

喜欢的同学可以关注公众号