青岛中联优谷

V8 引擎怎样对属性进行快速访问

2017-09-21

青岛网站建设

在这篇文章中我将要解释 V8 引擎内部是如何处理 JavaScript 属性的。从 JavaScript 的角度来看,属性们区别并不大,JavaScript 对象表现形式更像是字典,字符串作为键,任意对象作为值ECMAScript 语言规范 中,对象的数字索引和其他类型索引在规范中没有明确区分,但是在 V8 引擎内部却不是这样的。除此之外,不同属性的行为基本相同,和他们可不可以进行整数索引没有关系。

然而在 V8 引擎中属性的不同表现形式确实会对性能和内存有影响,在这篇文章中我们来解析 V8 引擎是如何能够在动态添加属性时进行快速的属性访问的,理解属性是如何工作的,以解释 V8 引擎是如何的优化,(例如 内联缓存 )。

这篇文章解释了处理整数索引属性和命名属性的不同之处,之后我们展示了 V8 中是如何为了提供一个快速的方式定义一个对象的模型在添加一个命名属性时使用 HiddenClasses。然后,我们将继续深入了解如何根据使用情况进行属性名的命名优化,以便能够快速访问或者快速修改。在最后一节中,我们介绍 V8 如何处理整数索引属性或数组索引的详细信息。

命名属性和元素

让我们从分析一个非常简单的对象开始,比如:{a: "foo", b: "bar"}。这个对象有两个命名属性,"a" 和 "b"。它没有使用任何的整数索引作为属性名。我们也可以使用索引访问属性,特别是对象为数组的情况。例如,数组 ["foo", "bar"]有两个可以使用数组索引的属性:索引为 0 的值是 "foo",索引为 1 的值是 "bar"。

这是 V8 一般处理属性的第一个主要区别。

下图显示了一个 JavaScript 的基本对象在内存中的样子。

元素和属性存储在两个独立的数据结构中,这使得使用不同的模式添加和访问属性和元素将会更加高效。

元素主要用于各种 Array.prototype methods例如 pop或 slice。考虑到这些函数是在连续范围存储区域内访问属性的,V8 引擎内部大部分情况下也将他们表示为简单的数组。稍后我们将解释如何使用一个稀疏的基于字典的表示来节省内存。

命名属性的存储类似于稀疏数组的存储。然而,与元素不同,我们不能简单的使用键推断其在属性数组中的位置,我们需要一些额外的元数据。在 V8 中,每一个 JavaScript 对象都有一个相关联的 HiddenClass。这个 HiddenClass存储了一个对象的模型信息,在其他方面,有一个从属性名到属性索引映射。我们有时使用一个字典来代替简单的数组。我们专门会在一个章节中更详细地解释这一点。

本节重点:

  • 数组索引属性存储在单独的元素存储区中。
  • 命名属性存储在属性存储区中。
  • 元素和属性可以是数组或字典。
  • 每个 JavaScript 对象有一个和对象的模型相关联的 HiddenClass。

HiddenClasses 和描述符数组

在介绍了元素和命名属性的大致区别之后,我们需要来看一下 HiddenClasses 在 V8 中是怎么工作的。HiddenClass 存储了一个对象的元数据,包括对象和对象引用原型的数量。HiddenClasses 在典型的面向对象的编程语言的概念中和“类”类似。然而,在像 JavaScript 这样的基于原型的编程语言中,一般不可能预先知道类。因此,在这种情况下,在 V8 引擎中,HiddenClasses 创建和更新属性的动态变化。HiddenClasses 作为一个对象模型的标识,并且是 V8 引擎优化编译器和内联缓存的一个非常重要的因素。通过 HiddenClass 可以保持一个兼容的对象结构,这样的话实例可以直接使用内联的属性。

让我们来看一下 HiddenClass 的重点

在 V8 中,JavaScript 对象的第一部分就是指向 HiddenClass。(实际上,V8 中的任何对象都在堆中并且受垃圾回收器管理。)在属性方面,最重要的信息是第三段区域,它存储属性的数量,以及一个指向描述符数组的指针。描述符数组包含有关命名属性的信息,如名称本身和存储值的位置。注意,我们不在这里跟踪整数索引属性,因此描述符数组中没有整数索引的条目。

关于 HiddenClasses 的基本假设是对象具有相同的结构,例如,相同的顺序对应相同的属性,共用相同的 HiddenClass。当我们给一个对象添加一个属性的时候我们使用不同的 HiddenClass 实现。在下面的例子中,我们从一个空对象开始并且添加三个命名属性。

每次加入一个新属性时,对象的 HiddenClass 就会改变,在 V8 引擎的后台会创建一个将 HiddenClass 连接在一起的转移树。V8 引擎就知道你添加的 HiddenClass 是哪一个了,例如,属性 “a” 添加到一个空对象中,如果你以相同的顺序添加相同的属性,这个转化树会使用相同的 HiddenClass。下面的示例表明,即使在两者之间添加简单的索引属性,我们也将遵循相同的转换树。

本节重点:

  • 结构相同的对象(相同的顺序对于相同的属性)有相同的 HiddenClasses。
  • 默认情况下,每添加一个新的命名属性将产生了一个新的 HiddenClasses。
  • 增加数组索引属性并不创造新 HiddenClasses。