垃圾回收
栈内存回收
通过 ESP(记录当前执行状态的指针)下移,使得保存在 ESP 指针上面的执行上下文内存失效,从而可以被替换。
堆内存回收
堆内存的垃圾回收需要用到 JavaScript 的垃圾回收器。
代际假说
- 大部分对象在内存中存在的时间很短,简单来说,就是很多对象一经分配内存,很快就变得不可访问;
- 不死的对象,会活得更久。
基于代际假说,V8 将堆分成新生代和老生代两块,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的对象。对于这两块区域,V8 分别使用两个不同的垃圾回收器,以便更高效地实施垃圾回收。
- 副垃圾回收器,主要负责新生代的垃圾回收。
- 主垃圾回收器,主要负责老生代的垃圾回收。
标记清除
GC 执行流程:
- 标记空间中活动对象和非活动对象。
- 回收非活动对象所占据的内存。
- 内存整理(副垃圾回收器不需要)。
副垃圾回收器
Scavenge 算法:新生代分为对象区域和空闲区域,通过复制交换完成垃圾回收。新生代由于空间小,还采用了对象晋升策略,既两次 GC 还存活的对象移动到老生代。
主垃圾回收器
Mark-Compact 算法:对于大对象和老对象,Scavenge 算法效率低,浪费空间。所以采用标记清除算法,为了消除多次 Mark-Sweep 产生的内存碎片,进而采用 Mark-Compact 算法。
全停顿
JavaScript 在垃圾回收时会暂停脚本执行(Stop-The-World)。为了优化体验,又提出了增量标记 Incremental-Marking 算法,又叫并发标记。允许 GC 扫描和标记对象时,同时执行 JS 代码。
没有银弹
其实站在工程师的视角,我们经常需要在满足需求的前提下,权衡各个指标的得失,把系统设计得尽可能适应最核心的需求。