V8 内联缓存如何与删除和计算的 属性 访问一起使用?
How does V8 inline caching work with delete and computed property access?
如 https://github.com/v8/v8/wiki/Design%20Elements#fast-property-access 中的示例,我(试图)理解对象的属性存储在其构造函数的隐藏 class 中,称之为 "C[N]"。我可能没有理解正确...例如:
// Let's suppose Object has these hidden classes already
/* Object[[HiddenClasses]] > C0, C1, C2
*
* C0 - for "x", goto C1
* C1 - "x"; for "y", goto C2
* C2 - "x", "y";
*/
var obj = {
x: 0
};
// Currently based in C1 to get/put properties
obj.y = 0;
// Now based in C2
1。如果这个新对象添加一个新的 属性 会怎样?
obj.z = 0
它的行为是否仍像 Object
的第一个实例?
2。如果同一构造函数的对象以与隐藏 classes 相反的顺序添加属性,会发生什么情况?
({ y: 5; }); // Will this be based in C2?
3。如果删除 属性 会怎样?它的值是否仅在内存中更改以表示 deleted
?
4。当编译器看不到其计算的 属性 访问的对象或名称时,查找是否在隐藏的 class 内完成?即:
({ [Math.random()]: 0 }),
randomlyReceivedObject.property;
在obj.z = 0
之后,C2
得到一个新的转换for "z", goto C3
,并为classC3 - "x", "y", "z"
创建一个新的隐藏对象.
该对象将有一个新的隐藏 class C4 - "y"
。 C0
得到一个新的转换:for "y", goto C4
。如果该对象稍后获得 "x" 属性,将创建一个新的隐藏 class C5 - "y", "x"
,并在 C4
中进行转换:for "x", goto C5
。 C5
和 C2
将永远保持不同。这实际上是由 JavaScript 规范规定的,因为例如a for..in
loop Object.getOwnPropertyKeys
必须按创建顺序迭代属性,V8 通过隐藏的 classes 跟踪 属性 创建顺序。
由于拥有更多的隐藏 classes 自然会增加速度和内存成本,因此通常建议始终以相同的顺序创建属性(例如,通过使用构造函数)。
当一个属性被删除时,对象被置于字典模式,隐藏的classes不再被用来跟踪它的属性。在内部这是通过一个特殊的隐藏 class: C6 - all properties are in the properties dictionary
实现的。随后的每个 属性 访问都需要进行字典查找。如果你想避免这种情况,你可以手动覆盖属性而不是删除它们:obj.x = null
或类似的。 (细则:在最近的 V8 版本中,有一个例外,如果 last 属性 被删除,那么最后一个隐藏的 class 转换已回滚。此实现细节将来可能会保留,也可能不会保留,所以不要依赖它。)
隐藏的 classes 总是用于查找。内联缓存只是缓存这些查找的结果;这总是发生在执行时,而不是编译时。 "The compiler"一般不能"see"对象,因为在编译时还没有对象。
如 https://github.com/v8/v8/wiki/Design%20Elements#fast-property-access 中的示例,我(试图)理解对象的属性存储在其构造函数的隐藏 class 中,称之为 "C[N]"。我可能没有理解正确...例如:
// Let's suppose Object has these hidden classes already
/* Object[[HiddenClasses]] > C0, C1, C2
*
* C0 - for "x", goto C1
* C1 - "x"; for "y", goto C2
* C2 - "x", "y";
*/
var obj = {
x: 0
};
// Currently based in C1 to get/put properties
obj.y = 0;
// Now based in C2
1。如果这个新对象添加一个新的 属性 会怎样?
obj.z = 0
它的行为是否仍像 Object
的第一个实例?
2。如果同一构造函数的对象以与隐藏 classes 相反的顺序添加属性,会发生什么情况?
({ y: 5; }); // Will this be based in C2?
3。如果删除 属性 会怎样?它的值是否仅在内存中更改以表示 deleted
?
4。当编译器看不到其计算的 属性 访问的对象或名称时,查找是否在隐藏的 class 内完成?即:
({ [Math.random()]: 0 }),
randomlyReceivedObject.property;
在
obj.z = 0
之后,C2
得到一个新的转换for "z", goto C3
,并为classC3 - "x", "y", "z"
创建一个新的隐藏对象.该对象将有一个新的隐藏 class
C4 - "y"
。C0
得到一个新的转换:for "y", goto C4
。如果该对象稍后获得 "x" 属性,将创建一个新的隐藏 classC5 - "y", "x"
,并在C4
中进行转换:for "x", goto C5
。C5
和C2
将永远保持不同。这实际上是由 JavaScript 规范规定的,因为例如afor..in
loopObject.getOwnPropertyKeys
必须按创建顺序迭代属性,V8 通过隐藏的 classes 跟踪 属性 创建顺序。 由于拥有更多的隐藏 classes 自然会增加速度和内存成本,因此通常建议始终以相同的顺序创建属性(例如,通过使用构造函数)。当一个属性被删除时,对象被置于字典模式,隐藏的classes不再被用来跟踪它的属性。在内部这是通过一个特殊的隐藏 class:
C6 - all properties are in the properties dictionary
实现的。随后的每个 属性 访问都需要进行字典查找。如果你想避免这种情况,你可以手动覆盖属性而不是删除它们:obj.x = null
或类似的。 (细则:在最近的 V8 版本中,有一个例外,如果 last 属性 被删除,那么最后一个隐藏的 class 转换已回滚。此实现细节将来可能会保留,也可能不会保留,所以不要依赖它。)隐藏的 classes 总是用于查找。内联缓存只是缓存这些查找的结果;这总是发生在执行时,而不是编译时。 "The compiler"一般不能"see"对象,因为在编译时还没有对象。