浅谈V8引擎中的垃圾回收机制(3)

在执行Scavenge之前,From区长这幅模样

+---+---+---+---+---+---+---+---+--------+ | A | B | C | D | E | F | G | H | | +---+---+---+---+---+---+---+---+--------+

那么首先将根对象能到达的ABC对象复制到To区,于是乎To区就变成了这个样子:

allocationPtr ↓ +---+---+---+----------------------------+ | A | B | C | | +---+---+---+----------------------------+ ↑ scanPtr

接下来进入循环,扫描scanPtr所指的A对象,发现其没有指针,于是乎scanPtr移动,变成如下这样

allocationPtr ↓ +---+---+---+----------------------------+ | A | B | C | | +---+---+---+----------------------------+ ↑ scanPtr

接下来扫描B对象,发现其有指向E对象的指针,且E对象在From区,那么我们需要将E对象复制到allocationPtr所指的地方并移动allocationPtr指针:

allocationPtr ↓ +---+---+---+---+------------------------+ | A | B | C | E | | +---+---+---+---+------------------------+ ↑ scanPtr

B对象里所有指针都已被复制完,所以移动scanPtr:

allocationPtr ↓ +---+---+---+---+------------------------+ | A | B | C | E | | +---+---+---+---+------------------------+ ↑ scanPtr

接下来扫描C对象,C对象中有两个指针,分别指向F对象和G对象,且都在From区,先复制F对象到To区:

allocationPtr ↓ +---+---+---+---+---+--------------------+ | A | B | C | E | F | | +---+---+---+---+---+--------------------+ ↑ scanPtr

然后复制G对象到To区

allocationPtr ↓ +---+---+---+---+---+---+----------------+ | A | B | C | E | F | G | | +---+---+---+---+---+---+----------------+ ↑ scanPtr

这样C对象内部的指针已经复制完成了,移动scanPtr:

allocationPtr ↓ +---+---+---+---+---+---+----------------+ | A | B | C | E | F | G | | +---+---+---+---+---+---+----------------+ ↑ scanPtr

逐个扫描E,F对象,发现其中都没有指针,移动scanPtr:

allocationPtr ↓ +---+---+---+---+---+---+----------------+ | A | B | C | E | F | G | | +---+---+---+---+---+---+----------------+ ↑ scanPtr

扫描G对象,发现其中有一个指向H对象的指针,且H对象在From区,复制H对象到To区,并移动allocationPtr:

allocationPtr ↓ +---+---+---+---+---+---+---+------------+ | A | B | C | E | F | G | H | | +---+---+---+---+---+---+---+------------+ ↑ scanPtr

完成后由于G对象没有其他指针,且H对象没有指针移动scanPtr:

allocationPtr ↓ +---+---+---+---+---+---+---+------------+ | A | B | C | E | F | G | H | | +---+---+---+---+---+---+---+------------+ ↑ scanPtr

此时scanPtr和allocationPtr重合,说明复制结束

可以对比一下From区和To区在复制完成后的结果:

//From区 +---+---+---+---+---+---+---+---+--------+ | A | B | C | D | E | F | G | H | | +---+---+---+---+---+---+---+---+--------+ //To区 +---+---+---+---+---+---+---+------------+ | A | B | C | E | F | G | H | | +---+---+---+---+---+---+---+------------+

D对象没有被复制,它将被作为垃圾进行回收

写屏障

如果新生代中的一个对象只有一个指向它的指针,而这个指针在老生代中,我们如何判断这个新生代的对象是否存活?为了解决这个问题,需要建立一个列表用来记录所有老生代对象指向新生代对象的情况。每当有老生代对象指向新生代对象的时候,我们就记录下来

对象的晋升

当一个对象经过多次新生代的清理依旧幸存,这说明它的生存周期较长,也就会被移动到老生代,这称为对象的晋升。具体移动的标准有两种:
1. 对象从From空间复制到To空间时,会检查它的内存地址来判断这个对象是否已经经历过一个新生代的清理,如果是,则复制到老生代中,否则复制到To空间中
2. 对象从From空间复制到To空间时,如果To空间已经被使用了超过25%,那么这个对象直接被复制到老生代

老生代 老生代的特点

老生代所保存的对象大多数是生存周期很长的甚至是常驻内存的对象,而且老生代占用的内存较多

老生代的垃圾回收算法

老生代占用内存较多(64位为1.4GB,32位为700MB),如果使用Scavenge算法,浪费一半空间不说,复制如此大块的内存消耗时间将会相当长。所以Scavenge算法显然不适合。V8在老生代中的垃圾回收策略采用Mark-Sweep和Mark-Compact相结合

Mark-Sweep(标记清除)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/96a7d1313d3f7e3fc8b9f194101ec007.html