chrome的垃圾回收

465 阅读3分钟

v8引擎的垃圾回收可分为栈内存回收和堆内存回收。

一、栈内存回收

在执行js代码的流程中,js引擎会为函数创建执行上下文,并压入调用栈中。

有一个指针指向调用栈的栈顶,用于记录当前执行状态。

当一个函数的执行上下文出栈时,指针向下移动,下移的过程就是删除上下文的过程,原始变量就会被删除。

二、堆内存回收

原始内容回收了,但是对象还没有,它们还存在在堆中。

对于堆中内存的回收主要是使用主垃圾回收器、副垃圾回收器。v8将堆中空间分成两份相同的空间,一份新生区,一份老生区。

新生代空间存储生存时间较短的对象,老生代存储生存时间较长的对象。

主垃圾回收器主要负责回收老生代的内存。

副垃圾回收器主要负责回收新生代的内存。

1、副垃圾回收器

新生区被分为两部分:对象区域、空闲区域。

  • 新加入的对象被直接放入对象区域,当对象区域快要被写满时,就要执行一次垃圾回收。
  • 首先,进入标记阶段,标记出存活对象和非存活对象。
  • 然后进入清除阶段,将存活对象复制到空闲区域,在这个过程中,副垃圾回收器会对复制的这些内容进行排序,所以在空闲区域不存在内存碎片。
  • 在复制完之后,对象区域和空闲区域调换,这样就完成了新生区的垃圾回收。

因为复制需要成本,所以新生区不会被设置的太大,因此只能存一个小对象。

有些对象存活的时间比较长,会造成新生区的内存呗占满,因此v8使用的策略是对象晋升。

对象晋升策略:通过两次垃圾回收还存活的对象,就直接存入老生区。

2、主垃圾回收器

老生区的对象有两个特点:

  • 对象占用的空间大。
  • 存活的时间长。

主垃圾回收器使用标记-清除法进行回收空间。

标记清除法:

当调用栈中的函数执行上下文出栈时,遍历整个调用栈,被引用的被标记为存活对象,未被引用的标记为回收对象,然后清除。

但是标记清除法存在的问题是:存在大量的内存碎片。

所以提出了标记-整理法:

标记的过程和标记清除法的过程一致,不过并不是直接清除回收对象,而是让存活的对象向一端移动,然后清理边界之外的内存。

三、全停顿

一旦开始垃圾清除,js线程就要停下,不在继续执行js代码,而是转而执行垃圾回收,等垃圾回收执行完了,在执行js代码,这样就叫做全停顿

全停顿时间过长会造成页面的响应和性能直线下降,容易造成页面的卡顿。

为了解决这个问题,v8引擎将老生代的垃圾标记阶段进行拆分,标记和js代码执行交替进行,标记完了在进行清除(整理)。