V8 垃圾回收
// Firefox GC:浏览器 Console 中(指定进程) Components.utils.forceGC()
Chrome 41 版本包括了一个针对渲染引擎的任务调度器(Task Scheduler),以确保Chrome浏览器一直保持响应和流畅,任务调度器使延迟敏感的任务拥有更高的优先级。垃圾回收就是一种典型空闲任务。
V8 采用了一个分代(Generational)垃圾回收器[v8 blog],将内存堆分割为新生代(Young Generation)和老生代(Old Generation)。新生代的对象为存活时间较短的对象,老生代中的对象为存活时间较长或常驻内存的对象。由于绝大多数对象的生存期很短,只有少数对象的生存期较长,这种分代策略能使垃圾回收器对新生代对象执行一些规则的、小的垃圾回收(被称为 Scavenge)。V8 分别对新生代对象和老生代对象使用不同的垃圾回收算法来提升垃圾回收的效率。
新生代对象的 Scavenge 操作的持续时间取决于新生代中活跃对象的数量。在大部分新生代对象活跃时间不长的情况下,一个 Scavenge 操作非常快(<1ms)。
V8 在老生代中的垃圾回收采用标记-清除(Mark-Sweep)(不是引用计数)和 Mark-Compact 相结合的策略。当老生代中的活动对象增长超过了一个预设的限制的时候,将对堆栈执行一个大回收。将完整的标记拆分成很多小的步骤,以免时间太长让浏览器失去响应。标记完成后,由专门的清扫线程同步执行。当内存碎片成为问题时执行内存紧缩(Memory Compaction)
Scavenge 的具体实现中,采用 Cheney 算法—— 一种采用复制方式实现的垃圾回收算法:
- 将堆内存一分为二,每个部分空间称为 semispace;
- 两个 semispace 一个处于使用中(称为 From 空间),一个处于空闲状态(称为 To 空间);
- 当我们分配对象时,在 From 空间进行分配;
- 当开始进行垃圾回收时,会检查 From 空间的存活(可达)对象,把它们复制到 To 空间,而非存活对象占用的空间被释放;
- 完成复制后,From 空间和 To 空间角色对换。
Scavenge 的缺点是只能使用堆内存的一半,但由于只复制存活对象,并且由于生命周期短的场景中存活对象只占少部分,所以它在时间效率上不错。