V8垃圾回收策略
V8的垃圾回收策略
- V8的垃圾回收策略主要是基于分代式回收机制,其根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同的分代采用不同的垃圾回收算法。
V8的内存结构
- 在V8引擎的堆结构组成中,其实除了新生代和老生代外,但是垃圾回收的过程主要出现在新生代和老生代。V的内存结构主要室由以下几个部分组成:
- 新生代(new_space): 大多数的对象开始都会被分配在这里,这个区域相对较小但是垃圾回收特别频繁,该区域被分为两半,一半用来分配内存,另一半同于在垃圾回收时将需要保留的对象复制过来。
- 老生代(old_space): 新生代中的对象在存活一段时间后就会被转移到老生代内存区,相对于新生代该内存区域的垃圾回收频率较低。老生代又分为老生代指针区和老生代数据区,前者包含大多数可能存在指向其他对象的指针的对象,后者只保存原始数据对象,这些对象没有指向其他对象的指针。
- 大对象区(large_object_space): 存放体积超越其他区域大小的对象,每个对象都会有自己的内存,垃圾回收不会移动大对象区。
- 代码区(code_space): 代码对象,会被分配在这里,唯一拥有执行权限的内存区域。
- map区(map_space): 存放Cell和Map,每个区域都是存放相同大小的元素,结构简单。
新生代
- 在V8引擎的内存结构中,新生代主要用于存放存活时间较短的对象。新生代内存是由两个semispace(半空间)构成的,内存最大值在 64 位系统和 32 系统上面分别为 32MB 和 16MB ,在新生代的垃圾回收过程主要采用了Scavenge算法。
- Scavenge算法是一种典型的牺牲空间换取时间的算法,对于老生代内存来说,可能会存储大量对象,会导致内存资源的浪费,新生代内存中生命周期较短,使用这种算法,时间效率表现比较可观。
- 在Scavenge算法的具体实现中,主要采用了 Cheney算法,它将新生代内存一分为二,每一个部分的空间称为semispace,将new_space中划分的两个区域,其中处于激活状态的区域称为From空间,未激活(inactive new space)的区域我们称为To空间。这两个空间中,始终只有一个处于使用状态,另一个处于闲置状态。程序中声明的对象首先会被分配到From空间,当进行垃圾回收时,如果From空间中尚有存活对象,则会被复制到To空间进行保存,非存活的对象会被自动回收。当复制完成后,From空间和 To空间完成一次角色互换,To空间会变为新的From空间,原来的From空间则变为To空间。
老生代
- Mark-Sweep分为标记和清除两个阶段,在标记阶段会遍历堆中的所有对象,然后标记活的对象,在清除阶段,会将死亡的对象进行清除。Mark-Sweep算法主要通过判断某个对象是否可以被访问到,从而知道该对象是否应该被收回
- 垃圾回收器会在内部构建一个根列表,用于从根节点出发去寻找那些可以被访问到的变量。比如在JS中,window全局对象可以看成一个根节点。
- 然后,垃圾回收器从所有根节点出发,遍历其可以访问到的子节点,并将其标记为活动的,根节点不能到达的地方即为非活动的,将会被视为垃圾
- 最后,垃圾回收器将会释放所有非活动的内存块
如何避免内存泄漏
- 尽可能少地创建全局变量
- 手动清除定时器
- 少用闭包
- 清除DOM引用
- 弱引用