【V8】【1. 内存布局、隐藏类Hidden Class】

news2024/11/25 3:02:04

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

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

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]}`)
}


index:1  value:test-1
index:3  value:test-3
index:5  value:test-5
index:8  value:test-8
index:9  value:test-9
index:50  value:test-50
index:100  value:test-100
index:B  value:bar-B
index:A  value:bar-A
index:C  value:bar-C

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

在 V8 内部,为了有效地提升存储和访问这两种属性的性能,分别使用了两个线性数据结构来分别保存排序属性和常规属性,具体结构如下图所示:
在这里插入图片描述
如果执行索引操作,那么 V8 会先从 elements 属性中按照顺序读取所有的元素,然后再在 properties 属性中读取所有的元素,这样就完成一次索引操作。

快属性和慢属性

v8 中将部分常规属性直接存储到对象本身,我们把这称为对象内属性
在这里插入图片描述
对象内属性的数量是固定的,默认是 10 个,如果添加的属性超出了对象分配的空间,则它们将被保存在常规属性存储中。虽然属性存储多了一层间接层,但可以自由地扩容。

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

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

function Foo(property_num,element_num) {
    //添加可索引属性
    for (let i = 0; i < element_num; i++) {
        this[i] = `element${i}`
    }
    //添加常规属性
    for (let i = 0; i < property_num; i++) {
        let ppt = `property${i}`
        this[ppt] = ppt
    }
}
var bar = new Foo(20,10)

在这里插入图片描述
由于创建的常用属性超过了 10 个,所以另外 10 个常用属性就被保存到 properties 中了,注意因为 properties 中只有 10 个属性,所以依然是线性的数据结构,我们可以看其都是按照创建时的顺序来排列的。

所以这时候属性的内存布局是这样的:

  • 10 属性直接存放在 bar2 的对象内 ;
  • 10 个常规属性以线性数据结构的方式存放在 properties 属性里面 ;
  • 10 个数字属性存放在 elements 属性里面。

我们不建议使用 delete 来删除属性,因为删除后需要移动元素,开销较大,而且可能需要将慢属性重排到快属性。
如果删除属性在properties对象中,查找开销较大。

隐藏类 HiddenClass

V8 为了提升 JavaScript 的执行速度,借鉴了很多静态语言的特性,比如实现了 JIT 机制,为了提升对象的属性访问速度而引入了隐藏类,为了加速运算而引入了内联缓存。

我们知道,JavaScript 在运行时,对象的属性是可以被修改的,所以当 V8 使用了一个对象时,比如使用了 start.x 的时候,它并不知道该对象中是否有 x,也不知道 x 相对于对象的偏移量是多少,也可以说 V8 并不知道该对象的具体的形状。

静态语言的查询效率这么高,那么是否能将这种静态的特性引入到 V8 中呢?

答案是可行的。

目前所采用的一个思路就是将 JavaScript 中的对象静态化,也就是 V8 在运行 JavaScript 的过程中,会假设 JavaScript 中的对象是静态的,具体地讲,V8 对每个对象做如下两点假设:

  • 对象创建好了之后就不会添加新的属性;
  • 对象创建好了之后也不会删除属性。

V8 会为每个对象创建一个隐藏类,对象的隐藏类中记录了该对象一些基础的布局信息,包括以下两点:

  • 对象中所包含的所有的属性;
  • 每个属性相对于对象的偏移量。
let point = {x:100,y:200}

当 V8 执行到这段代码时,会先为 point 对象创建一个隐藏类,在 V8 中,把隐藏类又称为 map,每个对象都有一个 map 属性,其值指向内存中的隐藏类。

隐藏类描述了对象的属性布局,它主要包括了属性名称和每个属性所对应的偏移量,比如 point 对象的隐藏类就包括了 x 和 y 属性,x 的偏移量是 4,y 的偏移量是 8。

在这里插入图片描述

多个对象共用一个隐藏类

每次给对象添加了一个新属性之后,该对象的隐藏类的地址都会改变,这也就意味着隐藏类也随着改变了。

let point = {};
%DebugPrint(point);
point.x = 100;
%DebugPrint(point);
point.y = 200;
%DebugPrint(point);


DebugPrint: 0x986080c5b35: [JS_OBJECT_TYPE]
 - map: 0x0986082802d9 <Map(HOLEY_ELEMENTS)> [FastProperties]
 - ...


DebugPrint: 0x986080c5b35: [JS_OBJECT_TYPE]
 - map: 0x098608284ce9 <Map(HOLEY_ELEMENTS)> [FastProperties]
 - ...
 - properties: 0x0986080406e9 <FixedArray[0]> {
    #x: 100 (const data field 0)
 }


DebugPrint: 0x986080c5b35: [JS_OBJECT_TYPE]
 - map: 0x098608284d11 <Map(HOLEY_ELEMENTS)> [FastProperties]
 - p
 - ...
 - properties: 0x0986080406e9 <FixedArray[0]> {
    #x: 100 (const data field 0)
    #y: 200 (const data field 1) 

最佳实践

一,使用字面量初始化对象时,要保证属性的顺序是一致的。 比如先通过字面量 x、y 的顺序创建了一个 point 对象,然后通过字面量 y、x 的顺序创建一个对象 point2,代码如下所示:

let point = {x:100,y:200};
let point2 = {y:100,x:200};

虽然创建时的对象属性一样,但是它们初始化的顺序不一样,这也会导致形状不同,所以它们会有不同的隐藏类,所以我们要尽量避免这种情况。

二,尽量使用字面量一次性初始化完整对象属性。 因为每次为对象添加一个属性时,V8 都会为该对象重新设置隐藏类。

三,尽量避免使用 delete 方法。 delete 方法会破坏对象的形状,同样会导致 V8 为该对象重新生成新的隐藏类。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/760728.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python将dataframe数据导入MongoDB非关系型数据库

文章目录 pymongo连接新建数据库和集合pandas导入数据插入数据数据查看 pymongo连接 import pymongo client pymongo.MongoClient("mongodb://localhost:27017/") dblist client.list_database_names() for db in dblist:print(db) #查看已有数据库admin bilibil…

apply()函数--Pandas

1. DataFrame.apply()函数 1.1 函数功能 对DataFrame沿着指定轴运用函数。 函数接收到的对象是Series对象&#xff0c;它们的索引可以是DataFrame的行索引&#xff08;axis0&#xff09;或列索引&#xff08;axis1&#xff09;。结果为Series或DataFrame。 1.2 函数语法 Da…

java基础浮点类型

目录 1&#xff1a;float类型和double类型的定义 2&#xff1a;二者的范围和精度 3&#xff1a;float类型详解 3.1&#xff1a;整数位10进制转换为2进制 3.2&#xff1a;小数位10进制转换为2进制 3.3&#xff1a;把二进制放到浮点类型中 3.4&#xff1a;精度损失 4&…

手动下载composer项目放在vendor目录下并加载

比如添加easywechat。 说是手动&#xff0c;其实半手动。 到GitHub或gitee下载 1、下载后放在项目根目录下的vendor文件夹 2、在项目根目录的文件composer.json文件添加一段 "autoload": {"psr-4": {"EasyWeChat\\": "vendor/overtrue/wech…

9.带你入门matlab假设检验(matlab程序)

1.简述 函数 ztest 格式 h ztest(x,m,sigma) % x为正态总体的样本&#xff0c;m为均值μ0&#xff0c;sigma为标准差&#xff0c;显著性水平为0.05(默认值) h ztest(x,m,sigma,alpha) %显著性水平为alpha [h,sig,ci,zval] ztest(x,m,sigma,alpha,tail) %sig为观察…

3.5 Bootstrap 输入框组

文章目录 Bootstrap 输入框组基本的输入框组输入框组的大小复选框和单选插件按钮插件带有下拉菜单的按钮分割的下拉菜单按钮 Bootstrap 输入框组 本章将讲解 Bootstrap 支持的另一个特性&#xff0c;输入框组。输入框组扩展自 表单控件。使用输入框组&#xff0c;您可以很容易地…

SpringCloud Alibaba——Ribbon底层怎样实现不同服务的不同配置

目录 一、Ribbon底层怎样实现不同服务的不同配置二、源码角度分析 一、Ribbon底层怎样实现不同服务的不同配置 为不同服务创建不同的spring上下文&#xff0c;不同的spring上下文中存放对应这个服务所有的配置。 二、源码角度分析 SpringClientFactory中可以获取到所有ribbon…

自动化运维工具——Ansible学习(一)

目录 一、运维自动化发展历程及技术应用 (二)程序发布相关知识 (三)常用的自动化运维工具 二、Ansible入门 (一)Ansible发展史 (二)特点 (三)Ansible架构 (四)工作原理 (五)Ansible主要组成部分 (六) 安装步骤 1.各种安装方法与命令 (1)rpm包安装&#xff1a;EPEL源…

一篇文章让你学会Elasticsearch中的查询

还是惯例&#xff0c;开头先放章节目录&#xff0c;如果有帮到你的地方&#xff0c;欢迎点赞关注转发&#xff0c;如有错误&#xff0c;欢迎指出&#xff0c;不胜感激 一、环境初始化 version: 3.8 services:cerebro:image: lmenezes/cerebro:0.8.3container_name: cerebroport…

ylb-支付服务pay

总览&#xff1a; 在pay模块util包下&#xff0c;创建签名工具类Pkipair和http工具类HttpUtil&#xff1a; package com.bjpowernode.util;import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.securi…

centos7 docker 安装sql server 2019

contos7安装sql server docker最低1.8或更高 卸载旧的docker sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 装docker依赖包 #安装所需资源包 sudo yum install -…

mac 下 geoserver 安装

一、去官网下载geoserver https://geoserver.org/ 选择一个版本&#xff0c;然后点进去 二、需要配置java环境和设置geoserver 环境变量 1&#xff09;、java 环境安装 Java Downloads | Oracle 中国 2&#xff09;、环境变量设置 1.打开终端&#xff1a;command 空格键 2…

全卷积网络(FCN)

一&#xff1a;全卷积网络 义分割是对图像中的每个像素分类。 全卷积网络&#xff08;fully convolutional network&#xff0c;FCN&#xff09;采用卷积神经网络实现了从图像像素到像素类别的变换。与我们之前在图像分类或目标检测部分介绍的卷积神经网络不同&#xff0c;全卷…

融合自适应惯性权重和柯西变异的秃鹰搜索算法(CBES) - 附代码

融合自适应惯性权重和柯西变异的秃鹰搜索算法(CBES) 文章目录 融合自适应惯性权重和柯西变异的秃鹰搜索算法(CBES)1.秃鹰优化算法2.改进秃鹰优化算法2.1 Tent 映射2.2 自适应惯性权重2.3 柯西变异 3.实验结果4.参考文献5.Matlab代码6.python代码 摘要&#xff1a;针对基本秃鹰搜…

复习HashMap-2

在Java集合中&#xff0c;Map是一种特殊的集合&#xff0c;原因在于这种集合容器并不是保存单个元素&#xff0c;而是保存一个一个的Key-Vaue键值对.HashMap是基于哈希表的Map接口的实现,在项目开发中使用广泛,下面就对HashMap的源码进行解析. Hashmap的特点 1.HashMap是基于…

【Tauri + React 实战】VCluster - 配置应用图标与启动闪屏

本节我们将为 Tauri 应用自定义应用图标的启动闪屏 起步 通过 npm create tauri-applatest 我们成功创建了一个空白的 Tauri 项目&#xff0c;npm install安装好依赖后&#xff0c;通过 npm run tauri dev 即可开启热加载&#xff1a; 配置图标 官方示例很酷&#xff0c;但…

数仓报表数据导出——Hive数据导出至Clickhouse

1. Clickhouse建表 创建database create database ad_report; use ad_report;创建table drop table if exists dwd_ad_event_inc; create table if not exists dwd_ad_event_inc (event_time Int64 comment 事件时间,event_type String comment 事件…

运营实操,如何寻找自己产品的精准关键词

在进行Listing优化时&#xff0c;许多卖家感到困惑&#xff0c;不知从何入手。尽管他们听过许多关于优化的文章&#xff0c;但实际操作时仍不清楚何为好的标准。 亚马逊产品列表的另一个重要元素是图片。图像体验很大程度上决定了客户是否会继续浏览&#xff0c;仅仅依靠文字故…

Linux--查看man手册中某个函数的库函数

第一步&#xff1a;man 函数名 第二步&#xff1a;查看是否存在 第三步&#xff1a;如果不存在&#xff0c;则输入man 2 exit依次类推3、4.....

STM32开发笔记:中断

一、中断系统 中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行。 中断优先级&#x…