上文v8中快属性和慢属性讲到快属性和慢属性,搞清楚了v8中对象的属性查询是怎么样的。今天来看看为了加快对象的查询,v8还做了什么事情。
隐藏类
还是拿下面的构造函数来举例子
function Foo(elNum, pNum) {
for (let i = 0; i < elNum; i++) {
this[i] = 'ele' + i;
}
for (let i = 0; i < pNum; i++) {
this['pp' + i] = 'property' + i;
}
}
var a = new Foo(10, 10);
var b = new Foo(10, 10);
var c = new Foo(20, 10);
var j = new Foo(0, 10);
上述的实例中,我们看看它们的隐藏类是什么,elements和properties哪些会影响隐藏类的形状。
我们可以看到全部对象的map的指向都是@141629,说明上面几个对象的隐藏类是指向同一个。说明了elements的个数不影响隐藏类,隐藏类只记录properties的形状。
我们现在给a添加一个字段,看看隐藏类会有什么变化。
a.ppCustom = 'property comstom';
由于我刷新了页面,所以这里指向发生了变化,不过我们只看相对变化就可以。b/c/j的map都指向了@151345,而a的map变成了@151391。发生了变化,说明当对象的形状发生了变化的时候,隐藏类需要对应生成一个新的。
但是由于v8引擎考虑到了开发的时候既然会增加属性,也会有可能删除属性,所以map中有一个back_pointer字段,指向上一次map的地址
看看a的map的back_pointer,指向了@151345,跟大家的是一样的。如果后续执行又把ppCustom删除了,那么会沿着back_pointer链寻找匹配的隐藏类,如果匹配到则使用该隐藏类,避免了频繁增删属性导致频繁的生
成新的隐藏类。
隐藏类中存储了各个属性的偏移量
这样在查找属性的时候会先看隐藏类中的偏移量,然后直接取址获取数据。
我们再看看上图,这个对象(超过30个字符串属性)的map没有back_pointer和discriptor,说明当超过30个字符串属性的时候不再使用隐藏类。
结合之前的快属性的讲解,总结一下properties的查询:
- 如果初始化的时候字符串属性小于30个,则会生成隐藏类,查询通过隐藏类中的偏移量寻址取值,在已经存在隐藏类的情况下,新增的属性也会生成隐藏类;
- 如果初始化的时候字符串属性大于30个,则不会有隐藏类。初始化就有的字符串属性则直接通过外层直接获取,新增的属性则通过properties进行慢查询。
内联缓存
function load(o) {
return o.x;
}
通常v8获取o.x的流程是:查找对应对象o的隐藏类,再通过隐藏类查找x属性偏移量,然后取址获取值。
如果这个方法反复被执行,能不能不用经过隐藏类的查询就可以获取值呢?
答案是可以的
这个加速策略就是内联缓存。
v8在执行函数的过程中,会观察函数中的一些调用点(CallSite)的中间数据,然后将这些数据缓存起来,当下次执行的时候,直接利用这些中间数据获取值。有效提高重复代码的执行效率。
内联缓存为每个函数维护一个反馈向量,反馈向量其实是一个表结构,每一项称为插槽(slot)
slot | type | state | map | offset |
---|---|---|---|---|
0 | LOAD | MONO | 233214DkDK | 4 |
例如上述load
函数的反馈向量如上表所示。
slot表示插槽id,type有LOAD加载类型和STORE存储类型等,state有单态mono/多态poly/超态maga
当执行到调用点的时候,到对应的插槽中寻找属性的偏移量,大大提升了v8的执行效率。
var o = { x: 1, y: 2};
var o2 = { x: 2, y: 3, z: 4 };
load(o);
load(o2);
如果反复调用load
函数的时候,传入的对象隐藏类不同,那么会在同一个插槽中存储多个map信息。
slot | type | state | map | offset |
---|---|---|---|---|
0 | LOAD | MONO | 233214DkDK -> 888999DkDK | 4 -> 4 |
在一个插槽中如果只有一个map信息称为单态,有2-4个map信息称为多态,超过4个map信息称为超态。
所以在编码习惯上,最好相同数据结构的数据进行批量处理,减少不同数据结构的数据交叉处理的情况。
所以,都快2021年了,你拥抱TS了吗?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。