1. V8垃圾收集
- V8采用了一种分代回收的策略,将内存分为两个生代:新生代和老生代
新生代Scavenge
新生代采取Scavenge算法进行垃圾回收
- 扫描指针(scanPtr)和分配(allocationPtr)指针,分配指针总指向下一个待分配的指针,扫描指针是找当前节点是否有引用的对象
- 扫描的过程是一个广度优先的扫描过程
- 广度优先: 优先遍历兄弟节点
- 深度优先: 优先遍历自己
具体的方式:
首先将From空间中所有能从根对象到达的对象复制到To区,然后维护两个To区的指针scanPtr(扫描指针)和allocationPtr(分配指针),分别指向即将扫描的活跃对象和即将为新对象分配内存的地方,开始循环。循环的每一轮会查找当前scanPtr所指向的对象,确定对象内部的每个指针指向哪里。如果指向老生代我们就不必考虑它了。如果指向From区,我们就需要把这个所指向的对象从From区复制到To区,具体复制的位置就是allocationPtr所指向的位置。复制完成后将scanPtr所指对象内的指针修改为新复制对象存放的地址,并移动allocationPtr。如果一个对象内部的所有指针都被处理完,scanPtr就会向前移动,进入下一个循环。若scanPtr和allocationPtr相遇,则说明所有的对象都已被复制完,From区剩下的都可以被视为垃圾,可以进行清理了
晋升
- 当一个对象经过多次(一般为5次)新生代的清理依旧幸存,这说明它的生存周期较长,也就会被移动到老生代,这称为对象的晋升。具体移动的标准有两种:
-
- 对象从From空间复制到To空间时,会检查它的内存地址来判断这个对象是否已经经历过一个新生代的清理,如果是,则复制到老生代中,否则复制到To空间中
-
- 对象从From空间复制到To空间时,如果To空间已经被使用了超过25%,那么这个对象直接被复制到老生代
-
老生代
Mark-Sweep(标记清除)
- 在标记阶段需要遍历堆中的所有对象,并标记那些活着的对象,然后进入清除阶段。在清除阶段总,只清除没有被标记的对象。由于标记清除只清除死亡对象,而死亡对象在老生代中占用的比例很小,所以效率较高
- 特点: 虽然快,但会造成内存碎片,导致释放的内存的不连续,从而导致内存的浪费
Mark-Compact(标记整理)
- 在整理的过程中,将活着的 对象向内存区的一段移动,移动完成后直接清理掉边界外的内存
- 特点: 慢,但不会形成碎片 Chrome
V8的老生代使用标记清除和标记整理结合的方式,主要采用标记清除算法,如果空间不足以分配从新生代晋升过来的对象时,才使用标记整理