原作者:江凌

原文链接

Hidden Class 是为了实现对象属性的快速存取。

JavaScript是一种动态编程语言:属性可进行动态的添加和删除,这意味着一个对象的属性是可变的,大多数的JavaScript引擎使用一个类似字典的数据结构来存储对象的属性 —— 那么每个属性的访问都需要动态的去查询属性在内存中的位置。那么相比Java这样的静态语言来说就会慢的多。静态语言的属性地址会在类定义后通过编译,相对于对象有一个固定的偏移量,访问属性本质上只是简单的读取和存储,一条指令就可以搞定。

为了提高属性的访问速度, 在这种场景下,V8并没有动态的去内存中查询属性,而是动态的去创建Hidden Class 。Hidden Class 的基本想法并非什么新的创见,在prototype-based的编程语言Self中也做了类似的事情。An Efficient Implementation of Self, a Dynamically-Typed Object-Oriented Language Based on Prototypes在V8引擎中,当一个新的属性加入时,对象就会改变自己的Hidden Class 。

我们通过举例来更加清楚的了解整个过程,这个简单的JavaScript函数:

function Point(x, y) {this.x = x;this.y = y;
}

当你执行new Point(x, y)后, 一个新的Point的实例对象被创建了,首先V8会创建对象Point初始的Hidden Class ,在这个例子中,我们叫它C0. 这时这个对象中还没有任何属性被定义,初始的Hidden Class 为空。这一阶段,Point对象的Hidden Class 是C0.

然后执行第一条语句:Point (this.x = x;),这时会创建一个新的属性x,在这个案例中,V8会执行:

  • 基于C0创建另一个Hidden Class C1,并在C1中描述这个对象有一个属性x,它的值存储位置在Point对象的 offset 0
  • 当属性 x 已经被加入这个对象后,C1 会代替 C0 成为这个对象的Hidden Class 。

接着程序会执行第二条语句Point (this.y = y;), 创建一个新的属性 y, 类似的,V8会执行:

  • 基于C1创建另一个Hidden Class C2, 并在C2中描述这个对象还有一个属性y,它的值存储位置在Point对象的offset 1
  • 当属性 y 已经被加入这个对象后,C2 会代替 C1 成为这个对象的Hidden Class 。

在添加属性时同时创建一个新的 Hidden Class 看起来很低效。不过因为Hidden Class 是可以复用的,下一次再 new 一个Point的实例的时候,就不会重复的创建Hidden Class 了。

  • 如果对对象中的某个属性执行了delete操作
void LookupIterator::Delete() {Handle<JSObject> holder = Handle<JSObject>::cast(holder_);if (IsElement()) {ElementsAccessor* accessor = holder->GetElementsAccessor();accessor->Delete(holder, number_);} else {PropertyNormalizationMode mode = holder->map()->is_prototype_map()? KEEP_INOBJECT_PROPERTIES: CLEAR_INOBJECT_PROPERTIES;if (holder->HasFastProperties()) {JSObject::NormalizeProperties(holder, mode, 0, "DeletingProperty");holder_map_ = handle(holder->map(), isolate_);ReloadPropertyInformation();}// TODO(verwaest): Get rid of the name_ argument.JSObject::DeleteNormalizedProperty(holder, name_, number_);JSObject::ReoptimizeIfPrototype(holder);}
}

这里经过3个步骤,1)降级成字典模式 2)删除属性 3)再次尝试优化为快速模式,自然代价是非常高。

测试delete属性改变Hidden Class

其实还有很多情况让V8没法使用 Hidden Class 模式构建对象。 比如:

 function Point(x, y, z) {if (z) { this.x = x;} else {this.y = y;}}
  • 类似运行时属性赋值顺序不同,运行时属性缺失等情况都将破坏这种优化,退回到字典模式。
  • 当然快速属性也有一定的个数限制,查看代码object.cc,简单来说就是少于字典模式存储花费的3倍时为快速模式。

利用隐藏类有兩个好处:属性的访问不再需要字典查询,同时可以让V8使用一些在 class-based 的編程語言中经常用的优化方法,例如 inline caching。详见 Efficient Implementation of the Smalltalk-80 System

总结一下,Hidden Class 通过空间换时间,为对象设置Hidden Class,标记对象属性值存储位置相对于object的指针偏移,从而实现快速的访问属性值。

延伸阅读

查看隐藏类node-profiler

V8引擎中的Hidden Class相关推荐

  1. 浅谈V8引擎中的垃圾回收机制

    浅谈V8引擎中的垃圾回收机制 这篇文章的所有内容均来自 朴灵的<深入浅出Node.js>及A tour of V8:Garbage Collection,后者还有中文翻译版V8 之旅: 垃 ...

  2. JavaScript 工作原理(二)-如何在 V8 引擎中书写最优代码的 5 条小技巧(译)

    From:https://segmentfault.com/a/1190000014770145 原文请查阅这里,略有删减. 本系列持续更新中,Github 地址请查阅这里. 这是 JavaScrip ...

  3. Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

    V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 一.前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存.垃圾 ...

  4. JavaScript 工作原理之二-如何在 V8 引擎中书写最优代码的 5 条小技巧(译)

    原文请查阅这里,略有删减. 本系列持续更新中,Github 地址请查阅这里. 这是 JavaScript 工作原理的第二章. 本章将会深入谷歌 V8 引擎的内部结构.我们也会为如何书写更好的 Java ...

  5. 「译」Liftoff:V8 引擎中全新的 WebAssembly baseline 编译器

    翻译自:Liftoff: a new baseline compiler for WebAssembly in V8 Monday, August 20, 2018 V8 引擎在 v6.9 版本中加入 ...

  6. Google V8引擎浅析

    前端开发人员都会遇到一个流行词:V8.它的流行程度很大一部分是因为它将JavaScript的性能提升到了一个新的水平.是的,V8很快.但它是如何发挥它的魔力? 前言 源代码:https://sourc ...

  7. Chrome V8引擎介绍

    0.v8引擎出现的原因 这里先说一下什么是编译型语言和解释性语言: 编译型语言: 在程序执行之前必须进行专门的编译过程,有如下特点: 只须编译一次就可以把源代码编译成机器语言,后面的执行无须重新编译, ...

  8. 浏览器执行原理、V8引擎

    前言 对一个前端而言,思考JS在浏览器中如何被执行非常重要.笔者是通过codewhy的课程进行学习的,首先感谢codewhy. 浏览器的功能 浏览器的主要功能就是向服务器发出请求,在浏览器窗口中展示您 ...

  9. 浏览器执行原理-V8引擎

    浏览器内核 不同浏览器的内核组成 Gecko:早起被Netscape和Mozilla Firefox浏览器使用 Trident:微软开发,被IE4-IE11浏览器器使用,但在Edge已经转向Blink ...

最新文章

  1. springboot + elasticsearch
  2. matlab练习程序(简单图像融合)
  3. 图解Win7下set命令使用
  4. 【收藏】Win10:路径长度超过260个字符
  5. P3178 [HAOI2015]树上操作
  6. 通过从全局和类内部重载operator new /delete来获取内存管理权
  7. oppoJava面试!传智播客java基础案例教程
  8. 信号量进程同步与互斥
  9. 【springboot 踩坑记录】拦截器中无法注入bean
  10. html5 动态3d箭头,HTML5旋转的3D镐 | 箭头
  11. angularjs组件间通讯_angular组件间通讯的实现方法示例
  12. Oracle统计产生日志数据增长增量
  13. javascript性能
  14. 大数据时代激活数据管理新思路
  15. Linux文件上传下载sz 和 rz 命令
  16. mysql索引有几种_MySQL有哪些索引类型 ?
  17. 版本更新带来的缓存问题_【第1563期】缓存最佳实践 amp; maxage的陷阱
  18. kubernetes-dashboard部署
  19. 学习 storm,整合 springboot
  20. 根据关键词获取多平台的商品列表接口解析

热门文章

  1. 5-35V输入升压8-100V各种升压解决方案 大功率300W
  2. Pyautogui 实现键盘鼠标动作
  3. Python基础之线程(Thread)
  4. 请等待您的伙伴确认该请求_您想成为产品负责人吗?您最好知道等待什么
  5. Kuhn-Munkres(KM算法)简单笔记
  6. 实例简述Spring AOP之对AspectJ语法的支持
  7. 罗马数字表希腊字母表
  8. Field communityMapper in com.estate.service.impl.CommunityServiceImpl required a bean of type ‘
  9. vue组件走马灯_Vue-component | 文字走马灯组件
  10. C++ enable_if的使用