likes
comments
collection
share

v8垃圾回收机制

作者站长头像
站长
· 阅读数 10

前言

为什么要有垃圾回收机制

垃圾的产生 程序运行过程中变量的声明,都会占据一定的内存空间,当这些变量对应的值不再被访问时,这些变量及对应的值就没有意义,其对应的内存空间被浪费,那么这些空间就是垃圾。

内存占用 内存空间被占用的越多,系统运行就会越慢,甚至崩溃,所以就需要垃圾回收机制

代际假说

  1. 大部分对象在内存中存在的时间很短
  2. 不死的对象会活得更久

一.栈垃圾回收

首先,JS中的垃圾回收机制是自动回收的,不需要程序员进行额外的操作实现垃圾回收。

在栈中,JS引擎会通过移动ESP指针来销毁保存在栈中的执行上下文

二.堆垃圾回收

垃圾回收主要指的就是回收堆中的垃圾

1.堆分类

  • V8垃圾回收策略主要是基于分代式垃圾回收机制,其根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同的分代采用不同的垃圾回收算法。

存活时间长短不同的对象放在不同的堆位置,用不同的垃圾回收器(垃圾回收算法)

  • 新生代(存放生存时间短的对象) 副垃圾回收器 1~8M
  • 老生代(存放生存时间长的对象) 主垃圾回收器 v8垃圾回收机制

2.垃圾回收器的流程

无论是在哪个区域,回收流程都是一样的

  1. 标记空间中的活动对象和非活动对象(就相当于找出访问不到的对象)
  2. 回收非活动对象所占据的内存
  3. 内存整理(因为回收导致堆空间不连续)

3.副垃圾回收器(Scavege 算法)

通常小对象会先放在堆中的新生代中

  1. 将新生代区域划分为对象空间空闲空间,刚存入的对象放在对象空间
  2. 对象空间存满时,就会做一次回收,剩下没有回收的对象留了下来,没回收的对象复制空闲空间,接着对象空间空闲空间翻转,新存入的对象存在翻转后的空闲空间
  3. 符合一定添加还存活的对象,会被移动到老生代中(对象晋升策略)

堆的分类 v8垃圾回收机制 新生代回收流程图 v8垃圾回收机制

4.主垃圾回收器(标记清除算法)

存放存活时间长的对象(来自新生代晋级的对象),的对象

  1. 递归遍历整个调用栈,标记活动对象和垃圾数据
  2. 回收垃圾数据
  3. 内存整理

5.标记清除算法

标记清除分为标记清除两个阶段,在标记阶段会遍历堆中的所有对象,然后标记活着的对象,在清除阶段中,会将死亡的对象进行清除。Mark-Sweep 算法主要是通过判断某个对象是否可以被访问到,从而知道该对象是否应该被回收

  1. 垃圾回收器会在内部构建一个根列表,用于从根节点出发去寻找那些可以被访问到的变量。比如在 JavaScript 中,window 全局对象可以看成一个根节点。
  2. 垃圾回收器从所有根节点出发,遍历其可以访问到的子节点,并将其标记为活动的,根节点不能到达的地方即为非活动的,将会被视为垃圾。
  3. 垃圾回收器将会释放所有非活动的内存块,并将其归还给操作系统。

5.标记整理算法

但是经过标记清除之后的内存空间会⽣产很多不连续的碎⽚空间,这种不连续的碎⽚空间中, 在遇到较⼤的对象时可能会由于空间不⾜⽽导致⽆法存储。 为了解决内存碎⽚的问题,需要使⽤另外⼀种算法:标记-整理

过程:标记整理对待未存活对象不是⽴即回收,⽽是将存活对象移动到⼀边,然后直接清掉端边界以外的内存

6.全停顿-增量标记

由于 JS 运行在主线程,一旦垃圾回收算法生效,那么 JS 的执行就只能暂停,等待垃圾回收完成后再执行,会影响 JS 代码执行效率

为了解决这个问题,就出现了增量标记增量标记:将原本一次性遍历堆内存的操作改为先遍历堆内存对象中的一部分,然后暂停,让 JS 主线程先执行,再遍历标记堆内存对象中的一部分,循环直到全部遍历标记完。 即:把垃圾回收这个⼤的任务分成⼀个个⼩任务,穿插在 JavaScript 任务中间执⾏,让浏览器空闲时才标记,尽可能少的介绍对主线程的影响,提升性能。