V8引擎中的Hidden Class

简介:

原作者:江凌

原文链接

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.

1

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

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

2

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

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

3

在添加属性时同时创建一个新的 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

目录
相关文章
|
6月前
|
前端开发 索引
Layui 内置方法 - layer.style(重新定义层的样式)
Layui 内置方法 - layer.style(重新定义层的样式)
51 0
|
6月前
|
前端开发
Layui 内置方法 - layer.load(加载层)
Layui 内置方法 - layer.load(加载层)
228 0
|
9月前
|
容器
layui框架实战案例(23):在layui-tab-content中layui-progress-bar在html拼接中不显示lay-percent的解决方案
layui框架实战案例(23):在layui-tab-content中layui-progress-bar在html拼接中不显示lay-percent的解决方案
216 0
|
8天前
|
前端开发 JavaScript
Class 与 Style 绑定2
Class 与 Style 绑定2
|
6月前
Layui 内置方法 - layer.tips(tips层 )
Layui 内置方法 - layer.tips(tips层 )
89 0
|
9月前
|
JavaScript 前端开发 索引
|
9月前
【layui】layer.open常用参数解释
【layui】layer.open常用参数解释
|
12月前
|
前端开发 JavaScript
class与style绑定
class与style绑定
77 0
|
Java Spring
FileSystemResource和ClassPathResource有何区别?
FileSystemResource和ClassPathResource有何区别?
FileSystemResource和ClassPathResource有何区别?
《Drools7.0.0.Final规则引擎教程》第4章 4.2 auto-focus
《Drools7.0.0.Final规则引擎教程》第4章 4.2 auto-focus
131 0