V8实现了准确式GC,GC算法采用了分代式垃圾回收机制。因此,V8将内存(堆)分为新生代和老生代两部分。
(1) 新生代算法
新生代中的对象一般存活时间较短,使用Scavenge GC算法
在新生代空间中,内存空间分为两部分,分别为From空间和To空间。在这两个空间中,必定有一个空间是使用的,另外一个空间是空闲的。新分配的对象会被放入From空间,当From空间被占满时,新生代GC就会启动了。算法会检查From空间中存活的对象并复制到To空间中,如果有失活的对象就会被销毁。当复制完成后将From空间和To空间互换,这样GC就结束了。
(2)老生代算法
老生代中的对象一般存活时间较长且数量也多,使用了两个算法,分别是标记清除算法
和标记压缩算法
什么情况下对象会出现在老生代空间中:
老生代中的空间很复杂,有如下几个空间
enum AllocationSpace {
// TODO(v8:7464): Actually map this space's memory as read-only.
RO_SPACE, // 不变的对象空间
NEW_SPACE, // 新⽣代⽤于 GC 复制算法的空间
OLD_SPACE, // ⽼⽣代常驻对象空间
CODE_SPACE, // ⽼⽣代代码对象空间
MAP_SPACE, // ⽼⽣代 map 对象
LO_SPACE, // ⽼⽣代⼤空间对象
NEW_LO_SPACE, // 新⽣代⼤空间对象
FIRST_SPACE = RO_SPACE,
LAST_SPACE = NEW_LO_SPACE,
FIRST_GROWABLE_PAGED_SPACE = OLD_SPACE,
LAST_GROWABLE_PAGED_SPACE = MAP_SPACE
};
在老生代中,以下情况会先启动标记清除算法:
在这个阶段中,会遍历堆中所有的对象,然后标记活的对象,在标记完成后,销毁所有没有被标记的对象。
清除对象后会造成堆内存出现碎片的情况,当碎片超过一定限制后会启动压缩算法,在压缩过程中,将活的对象向一端移动,直到所有对象都移动完成然后清理掉不需要的内存。