JS中的原生数据类型存储在栈空间,引用类型数据存储在堆空间
调用栈中的数据如何回收?
ESP,一个记录当前执行状态的指针,指向调用栈中正在执行函数的执行上下文。
当一个函数执行结束后,js引擎会通过向下移动ESP来销毁该函数保存在栈中的执行上下文。
堆中的数据如何回收?
代际假说和分代收集
代际假说(The Generational Hypothesis) 两特点:
- 大部分对象在内存中存在的时间很短,即很多对象一经分配内存,很快就变的不可访问
- 不死的对象,会活的更久
在V8中会把堆分为新生代和老生代 ,对应的使用 副垃圾回收器、主垃圾回收器 实施垃圾回收。
- 新生代,存放生存时间短的对象, 使用副垃圾回收器
- 老生代,存放生存时间长的对象, 使用主垃圾回收器
垃圾回收器-工作流程
- 1、标记空间中的活动对象和非活动对象
- 2、回收非活动对象所占据的内存。即 在所有的标记完成后,统一清理内存中所有被标记为可回收的对象。
- 3、内存整理,因为 频繁回收对象后,导致内存中存在大量不连续空间(内存碎片),当要分配教大连续内存时,有可能出现内存不足情况。所有需要整理这些内存碎片。
副垃圾回收器
负责新生区的垃圾回收,通常情况下,大多数小的对象会被分配到新生区,因此垃圾回收比较频繁。
使用 Scavenge 算法,将空间对半划分为两个区域,一半是对象区域,一半是空闲区域。
- 新加入的对象会存放到对象区域
- 当对象区域快被写满时,执行一次垃圾清理操作
- 首先对 对象区域 中的垃圾做标记
- 标记完成后,进入垃圾清理阶段,副垃圾回收器 把 存活的对象 复制到空闲区域中,同时进行内存整理操作,将对象有序的排列。
- 复制完成后,对象区域和空闲区域角色翻转,重复此步骤
对象晋升策略,经过两次垃圾回收依然还存活的对象,会被移动到老生区
主垃圾回收器
老生区中的对象,有2个特点:
- 对象占用空间大
- 对象存活时间长
主垃圾回收器采用标记-清除/整理(Mark-Sweep-Compact)算法进行垃圾回收
- 标记,从一组根元素开始,递归变量这组跟元素,能到达的元素称为活动对象,没有到达的元素则判断为垃圾数据
- 整理,让所有存活的对象都向内存的一段移动
- 清除,清理掉端边界以外的内存
增量标记(Incremental Marking)算法
- 为降低老生代的垃圾回收而造成的卡顿
- V8 将一个完整的垃圾回收任务拆分为很多小的任务
- 让垃圾回收标记 和 js 应用逻辑 交替进行