垃圾回收
最后更新于
V8的垃圾回收策略主要是基于分代式垃圾回收机制
,其根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同的分代采用不同的垃圾回收算法。
在V8引擎的堆结构组成中,其实除了新生代
和老生代
外,还包含其他几个部分,但是垃圾回收的过程主要出现在新生代和老生代,所以对于其他的部分我们没必要做太多的深入,有兴趣的小伙伴儿可以查阅下相关资料,V8的内存结构主要由以下几个部分组成:
新生代(new_space)
:大多数的对象开始都会被分配在这里,这个区域相对较小但是垃圾回收特别频繁,该区域被分为两半,一半用来分配内存,另一半用于在垃圾回收时将需要保留的对象复制过来。
老生代(old_space)
:新生代中的对象在存活一段时间后就会被转移到老生代内存区,相对于新生代该内存区域的垃圾回收频率较低。老生代又分为老生代指针区
和老生代数据区
,前者包含大多数可能存在指向其他对象的指针的对象,后者只保存原始数据对象,这些对象没有指向其他对象的指针。
大对象区(large_object_space)
:存放体积超越其他区域大小的对象,每个对象都会有自己的内存,垃圾回收不会移动大对象区。
代码区(code_space)
:代码对象,会被分配在这里,唯一拥有执行权限的内存区域。
map区(map_space)
:存放Cell和Map,每个区域都是存放相同大小的元素,结构简单(这里没有做具体深入的了解,有清楚的小伙伴儿还麻烦解释下)。
上图中的带斜纹的区域代表暂未使用的内存,新生代(new_space)被划分为了两个部分,其中一部分叫做inactive new space,表示暂未激活的内存区域,另一部分为激活状态。
在V8引擎的内存结构中,新生代主要用于存放存活时间较短的对象。新生代内存是由两个semispace(半空间)
构成的,内存最大值在64
位系统和32
位系统上分别为32MB
和16MB
,在新生代的垃圾回收过程中主要采用了Scavenge
算法。
引用计数主要是IE等旧浏览器在使用,通过计数器分析变量的引用次数,清除没有引用到的变量。对于存在循环引用的情况则无法处理。比如
其中 o1 引用了 o2,o2 引用了 o1,在cycle函数执行完 o1,o2 都没有再次引用到,但是引用计数算法判断两者都存在引用。
用于V8中的新生代内存,将新生代内存一分为二:From 和 To,在From 和 To 之间转换的过程完成垃圾回收。
在老生代中,因为管理着大量的存活对象,如果依旧使用Scavenge
算法的话,很明显会浪费一半的内存,因此已经不再使用Scavenge
算法,而是采用新的算法Mark-Sweep(标记清除)
和Mark-Compact(标记整理)
来进行管理。
引用计数
的算法,两个变量均存在指向自身的引用,因此依旧无法被回收,导致内存泄漏。
因此为了避免循环引用导致的内存泄漏问题,截至2012年所有的现代浏览器均放弃了这种算法,转而采用新的Mark-Sweep(标记清除)
和Mark-Compact(标记整理)
算法
早期V8中堆内存采用的一种清除算法,全局扫描堆内存找出未使用到的对象进行标记并清除,由于未进行内存整理会存在内存碎片。
尽可能少的创建全局变量
手动清除定时器
少用闭包
清除DOM引用
使用弱引用