JS-V8垃圾回收

JS中的原生数据类型存储在栈空间,引用类型数据存储在堆空间

调用栈中的数据如何回收?

call stack-heap

ESP,一个记录当前执行状态的指针,指向调用栈中正在执行函数的执行上下文。
当一个函数执行结束后,js引擎会通过向下移动ESP来销毁该函数保存在栈中的执行上下文。

堆中的数据如何回收?

代际假说和分代收集

代际假说(The Generational Hypothesis) 两特点:

  • 大部分对象在内存中存在的时间很短,即很多对象一经分配内存,很快就变的不可访问
  • 不死的对象,会活的更久

在V8中会把堆分为新生代和老生代 ,对应的使用 副垃圾回收器、主垃圾回收器 实施垃圾回收。

  • 新生代,存放生存时间短的对象, 使用副垃圾回收器
  • 老生代,存放生存时间长的对象, 使用主垃圾回收器

垃圾回收器-工作流程

  • 1、标记空间中的活动对象和非活动对象
  • 2、回收非活动对象所占据的内存。即 在所有的标记完成后,统一清理内存中所有被标记为可回收的对象。
  • 3、内存整理,因为 频繁回收对象后,导致内存中存在大量不连续空间(内存碎片),当要分配教大连续内存时,有可能出现内存不足情况。所有需要整理这些内存碎片。
副垃圾回收器

负责新生区的垃圾回收,通常情况下,大多数小的对象会被分配到新生区,因此垃圾回收比较频繁
使用 Scavenge 算法,将空间对半划分为两个区域,一半是对象区域,一半是空闲区域。

  • 新加入的对象会存放到对象区域
  • 当对象区域快被写满时,执行一次垃圾清理操作
    • 首先对 对象区域 中的垃圾做标记
    • 标记完成后,进入垃圾清理阶段,副垃圾回收器 把 存活的对象 复制到空闲区域中,同时进行内存整理操作,将对象有序的排列。
    • 复制完成后,对象区域和空闲区域角色翻转,重复此步骤

对象晋升策略,经过两次垃圾回收依然还存活的对象,会被移动到老生区

主垃圾回收器

老生区中的对象,有2个特点:

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

主垃圾回收器采用标记-清除/整理(Mark-Sweep-Compact)算法进行垃圾回收

  • 标记,从一组根元素开始,递归变量这组跟元素,能到达的元素称为活动对象,没有到达的元素则判断为垃圾数据
  • 整理,让所有存活的对象都向内存的一段移动
  • 清除,清理掉端边界以外的内存
增量标记(Incremental Marking)算法
  • 为降低老生代的垃圾回收而造成的卡顿
  • V8 将一个完整的垃圾回收任务拆分为很多小的任务
  • 让垃圾回收标记 和 js 应用逻辑 交替进行