图解 Google V8 # 03:快属性和慢属性:V8是怎样提升对象属性访问速度的?

Posted 凯小默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解 Google V8 # 03:快属性和慢属性:V8是怎样提升对象属性访问速度的?相关的知识,希望对你有一定的参考价值。

说明

图解 Google V8 学习笔记

线性结构和非线性结构

javascript 中的对象是由一组组属性和值的集合,就像一个字典,字符串作为键名,任意对象可以作为键值,可以通过键名读写键值。


而在 V8 实现对象存储时,并没有完全采用字典的存储方式,而是用的非线性的数据结构,查询效率会低于线性的数据结构,V8 为了提升存储和查找效率,采用了一套复杂的存储策略。下面看看是用了什么策略去提升了对象属性的访问速度?

常规属性 (properties) 和排序属性 (element)

我们先看一个例子:

function Foo() 
    this[100] = 'test-100'
    this[1] = 'test-1'
    this["B"] = 'bar-B'
    this[50] = 'test-50'
    this[9] =  'test-9'
    this[8] = 'test-8'
    this[3] = 'test-3'
    this[5] = 'test-5'
    this["A"] = 'bar-A'
    this["C"] = 'bar-C'

var bar = new Foo()


for(key in bar)
    console.log(`index:$key  value:$bar[key]`)

我们无序的给函数添加属性然后打印出来,结果如下:

我们发现:

  1. 设置的数字属性被最先并且按照数字大小的顺序打印出来;
  2. 设置的字符串属性依然是按照之前的设置顺序打印的。

总的来说就是:一个对象中如果有数字型属性和非数字型属性,当遍历对象并打印出该对象的所有属性时,会优先把数字型属性按升序排序后打印,接着再对非数字型属性按添加的先后顺序打印出来。

原因:在 ECMAScript 规范中定义了数字属性应该按照索引值大小升序排列,字符串属性根据创建时的顺序排列。

  • 对象中的数字属性称为排序属性,在 V8 中被称为 elements
  • 字符串属性就被称为常规属性,在 V8 中被称为 properties

在 V8 内部,分别使用了两个线性数据结构来分别保存排序属性和常规属性,从而能有效地提升存储和访问这两种属性的性能,如图所示:

如果执行索引操作,那么 V8 会先从 elements 属性中按照顺序读取所有的元素,然后再在 properties 属性中读取所有的元素。

快属性和慢属性

对象内属性

将不同的属性分别保存到 elements 属性和 properties 属性中,简化了程序的复杂度,但是在查找元素时,却多了一步操作,会影响到元素的查找效率。

比如:上面执行 bar.B这个语句来查找 B 的属性值,那么在 V8 会先查找出 properties 属性所指向的对象 properties,然后再在 properties 对象中查找 B 属性

基于这个原因,V8 采取了一个权衡的策略:将部分常规属性直接存储到对象本身去加快查找属性的效率,我们把这称为对象内属性 (in-object properties)

比如:现在要执行 bar.B这个语句来查找 B 的属性值,那么 V8 就可以直接从 bar 对象本身去获取该值了。

对象内属性是默认10,超出部分会被存放在常规属性存储中保存,可以自由扩容。

快属性

通常,我们将保存在线性数据结构中的属性称之为快属性

因为线性数据结构中只需要通过索引即可以访问到属性,虽然访问线性结构的速度快,但是如果从线性结构中添加或者删除大量的属性时,则执行效率会非常低,这主要因为会产生大量时间和内存开销。

慢属性

如果一个对象的属性过多时,V8 就会采取另外一种存储策略,那就是慢属性策略,但慢属性的对象内部会有独立的非线性数据结构 (词典) 作为属性存储容器。所有的属性元信息不再是线性存储的,而是直接保存在属性字典中。

开启慢速模式时,使用hash作为底层存储结构,key为字符串,字面量会发生类型转换。

数组索引属性和命名属性存储在两个单独的数据结构中:

慢属性是如何存储的:

慢属性使⽤ HashMap 作为属性存储,⽽是直接存储在属性 Hash 中(没有缓存,所以叫慢属性)。

总结

参考资料

以上是关于图解 Google V8 # 03:快属性和慢属性:V8是怎样提升对象属性访问速度的?的主要内容,如果未能解决你的问题,请参考以下文章

图解 Google V8 # 06:原型链:V8是如何实现对象继承的?

V8中的快属性与内联缓存

V8中的快属性与内联缓存

图解Google V8,搞懂 JavaScript 执行逻辑

图解 Google V8 # 01:V8 是如何执行一段 JavaScript 代码的?

图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?