V8的垃圾回收 基于分代式垃圾回收机制
因为在实际的应用场景中,对象的生存周期长短不一样。不同的算法只能针对特定的情况具有最好的效果。
为此,统计学在垃圾回收算法的发展中产生了较大的作用,现代的垃圾回收算法中按对象的存活时间将内存的垃圾进行不同的分代,然后对不同的分代的内存实施更高效的算法。
V8的内存分代:主要分为新生代和老生代
Scavenge
算法,新生代的对象主要通过Scavenge算法进行垃圾回收,再具体实现中,采用Cheney算法。
Cheney算法是一种采用复制的方式实现的垃圾回收算法。它将堆内存一分为二,每一部分空间成为semispace。在两个semispace中一个处于使用中,一个处于闲置状态。处于使用中的称之为From空间,处于闲置状态的称之为To空间。当开始垃圾回收时,检查From空间中存活的对象,这些对象被复制到To空间,而非存活对象占用的空间被释放掉。完成复制后,From和To空间角色发生兑换。
当一个对象经过多次复制依然存活,它将会被认为是生命周期较长的对象,这种较长周期的对象随后会被移动到老生代中,采用新的算法进行管理。对象从新生代移动到老生代中的过程称之为晋升。
对象晋升的条件主要有两个: 1. 对象是否经历过Scavenge回收 2.To空间的内存占用比超过限制。
Mark-Sweep & Mark-Compact
Mark-Sweep 是标记清除的意思。 Mark-Sweep在标记阶段遍历堆中的所有对象,并标记活着的对象,在随后的清除阶段,只清除没有被标记的对象。 Mark-Sweep存在一个最大的问题就是,在进行一次标记清除回收之后,内存空间会出现不连续的状态。
为了解决内存碎片问题Mark-Compact被提出来。Mark-Compact是标记整理的意思。是在Mark-Sweep基础上演变出来的。它们的差别在于,在整理过程中,MC将活着的对象往一端移动,移动完成后,直接清理掉边界外的内存。
V8中两种算法是结合使用的。
Incremental Marking
为了避免出现JavaScript应用逻辑与垃圾回收器看起来不一样,垃圾回收的3种基本算法都需要将应用逻辑暂停下来,待执行完垃圾回收后再回复执行应用逻辑,这种行为被称为全停顿(stop-the-word).
为了降低全堆垃圾回收带来的停顿时间,V8从标记阶段入手,将原本一口气停顿完成的动作改为增量标记,每做完一个“步进”就让JavaScript应用逻辑执行一小会,垃圾回收与应用逻辑交替执行直到标记阶段完成。
V8经过增量标记之后,垃圾回收的最大停顿时间可以减少到原来的1/6
后续引入了延迟清理(lazy sweeping)增量整理(incremental compaction)