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

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

说明

图解 Google V8 学习笔记



线性结构和非线性结构

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

9739af9e15824b1cb147815bb080c108.png

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

cbcce03bc7a04f3e8e2367418ffe79a2.png


常规属性 (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]}`)
}


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


58ac59297cba4fa9aa471c6888fd2830.png


我们发现:


   设置的数字属性被最先并且按照数字大小的顺序打印出来;


   设置的字符串属性依然是按照之前的设置顺序打印的。


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

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


   对象中的数字属性称为排序属性,在 V8 中被称为 elements


   字符串属性就被称为常规属性,在 V8 中被称为 properties。


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

60a0f61a934548168ba4c7bae593ee4e.png

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



快属性和慢属性


对象内属性


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


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

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



d14932d308b74776ab04a47afbf91af8.png



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


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


快属性

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


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

慢属性


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


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


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


618ee5f2d3b140f4ad7393cc15c4ad83.png

慢属性是如何存储的:


20b6de4a879e41c2a509ef5514630501.png



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



总结

9a71ee861c664aab9f7987d930e245e2.png



目录
相关文章
|
2月前
|
索引
Google Earth Engine(GEE)——提取指定矢量集合中的NDVI值并附时间属性
Google Earth Engine(GEE)——提取指定矢量集合中的NDVI值并附时间属性
74 2
|
2月前
|
Web App开发 编解码 定位技术
Google Earth Engine(GEE)——土壤属性Soil Properties 800m分辨率
Google Earth Engine(GEE)——土壤属性Soil Properties 800m分辨率
28 0
|
2月前
|
NoSQL 数据库
Google Earth Engine(GEE)——美国大陆(CONUS)30米土壤属性概率图数据库
Google Earth Engine(GEE)——美国大陆(CONUS)30米土壤属性概率图数据库
45 0
|
Web App开发 缓存 JavaScript
图解 Google V8 # 13:字节码(一):V8为什么又重新引入字节码?
图解 Google V8 # 13:字节码(一):V8为什么又重新引入字节码?
236 0
图解 Google V8 # 13:字节码(一):V8为什么又重新引入字节码?
|
缓存 JavaScript 前端开发
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)
307 0
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)
|
Web App开发 JavaScript 前端开发
图解 Google V8 # 21 :垃圾回收(二):V8是如何优化垃圾回收器执行效率的?
图解 Google V8 # 21 :垃圾回收(二):V8是如何优化垃圾回收器执行效率的?
118 0
图解 Google V8 # 21 :垃圾回收(二):V8是如何优化垃圾回收器执行效率的?
|
算法 JavaScript Java
图解 Google V8 # 20 :垃圾回收(一):V8的两个垃圾回收器是如何工作的?
图解 Google V8 # 20 :垃圾回收(一):V8的两个垃圾回收器是如何工作的?
102 0
图解 Google V8 # 20 :垃圾回收(一):V8的两个垃圾回收器是如何工作的?
|
前端开发 JavaScript
图解 Google V8 # 19 :异步编程(二):V8 是如何实现 async/await 的?
图解 Google V8 # 19 :异步编程(二):V8 是如何实现 async/await 的?
132 0
图解 Google V8 # 19 :异步编程(二):V8 是如何实现 async/await 的?
|
消息中间件 前端开发 JavaScript
图解 Google V8 # 18 :异步编程(一):V8是如何实现微任务的?
图解 Google V8 # 18 :异步编程(一):V8是如何实现微任务的?
422 0
图解 Google V8 # 18 :异步编程(一):V8是如何实现微任务的?
|
消息中间件 程序员 Android开发
图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?
图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?
109 0
图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?