阅读 123

V8引擎系列(2):object model

本文是系列文章第二篇,详情见第一篇

object model用来优化对象的属性访问。

属性回顾

ecma规范将所有的对象定义为string key映射到property attributes的字典。

微信截图_20210829223517.png

Shapes

当不同对象有相同的属性keys,我们称它们有相同的shape,js引擎通过shape来优化对属性的访问。 如果我们将每个对象作为一个字典保存,会存在两个问题:

  1. 如何高效获取property attribute
  2. 相同shape的对象如果全量保存带来的信息冗余

因此,js引擎会单独保存shape,其会包含属性key和除[[value]]以外的attribute,以及用offset字段表示在对应对象中的位置。这样就解决了上述两个问题

image.png

Transition chains and trees

当我们在一个现有shape基础上添加新属性时,新的shape会保存新的属性,并利用transition chains连接原来的shape。每个shape可以被多个新的shape连接,构成树状。

当对象初始时没有属性时,初始的shape就是空shape。

但当一个对象有很多属性时,沿transition chains寻找某个属性时复杂度就变成了o(n),或者再退化为字典。

Inline Caches (ICs)

ic用来缓存对应shape指定属性的offset。

image.png

Validity cells

前面主要是讨论的对象本身的属性,而js的继承是基于原型的,而[[proto]]也是一个普通的属性,因此当我们读取原型上的属性时就要沿着原型链去查询。 比如

class Bar {
constructor(x) { this.x = x; }
getX() { return this.x; }
}
const foo = new Bar(true);
const x = foo.getX();
复制代码

读取getX时就需要三步

  1. 判断getX是否在本身
  2. 查找原型
  3. 判断getX是否在原型

image.png 即总的查找不熟需要1+2N,N是原型链上的原型数。 如果我们不保存原型对象本身,而是保存原型对象的shape,那么复杂度就会降为1+N,并在此基础上添加了inline cache。

每个原型的shape在这里都认为是独一无二的,不和其他对象共用, 并关联一个ValidityCell来表示原型对象本身或原型链上层是否修改

image.png 这意味着如果Object.prototype有修改,所有缓存都会无效,修改原型链本身就是不好的行为,除非在其他代码执行之前修改

image.png

当下一次查找时,如果cache命中,且ValidityCell有效,就可以直接在对应shape指定的offset读取。

array相关

其中array是一种特殊的对象,有自动更新的属性length。其index是有效范围0 to 2³²−2的数字,对应元素用对应字符串为key的属性表示,比如第0个属性的key为'0'。 由于每个index属性的attribute默认writable, enumerable, and configurable,因此就不会保存这些attribute信息,并且区别于其他命名属性保存。如果手动修改了某个元素的attribute,js引擎就需要使用字典另外保存这个元素,因此不要这么做。

参考

文章分类
前端
文章标签