垃圾回收

栈内存回收

通过 ESP(记录当前执行状态的指针)下移,使得保存在 ESP 指针上面的执行上下文内存失效,从而可以被替换。

堆内存回收

堆内存的垃圾回收需要用到 JavaScript 的垃圾回收器。

代际假说

  1. 大部分对象在内存中存在的时间很短,简单来说,就是很多对象一经分配内存,很快就变得不可访问;
  2. 不死的对象,会活得更久。

基于代际假说,V8 将堆分成新生代和老生代两块,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的对象。对于这两块区域,V8 分别使用两个不同的垃圾回收器,以便更高效地实施垃圾回收。

  • 副垃圾回收器,主要负责新生代的垃圾回收。
  • 主垃圾回收器,主要负责老生代的垃圾回收。

标记清除

GC 执行流程:

  1. 标记空间中活动对象和非活动对象。
  2. 回收非活动对象所占据的内存。
  3. 内存整理(副垃圾回收器不需要)。

副垃圾回收器

Scavenge 算法:新生代分为对象区域和空闲区域,通过复制交换完成垃圾回收。新生代由于空间小,还采用了对象晋升策略,既两次 GC 还存活的对象移动到老生代。

主垃圾回收器

Mark-Compact 算法:对于大对象和老对象,Scavenge 算法效率低,浪费空间。所以采用标记清除算法,为了消除多次 Mark-Sweep 产生的内存碎片,进而采用 Mark-Compact 算法。

全停顿

JavaScript 在垃圾回收时会暂停脚本执行(Stop-The-World)。为了优化体验,又提出了增量标记 Incremental-Marking 算法,又叫并发标记。允许 GC 扫描和标记对象时,同时执行 JS 代码。

没有银弹

其实站在工程师的视角,我们经常需要在满足需求的前提下,权衡各个指标的得失,把系统设计得尽可能适应最核心的需求。