JS 引擎比较

概述

本页用来纪录各个开源 JS 引擎(SpiderMonkey、V8、JavaScriptCore)在算法、未来趋势上的比较。除非额外说明,内存相关的数据假设系统为 32 位

类、函数、词汇比较表

为了让懂得其中一个 JS 引擎的程序员迅速了解另外一个引擎,以下整理这些引擎共同概念的实现比较(把鼠标放在类/函数上面可以找到定义该类/函数的文件):

常见

SpiderMonkey V8 备注
Value MaybeObject * 代表一个 JS 值(数值、字符串、对象……)的类。SpiderMonkey 是用 nun-boxing,总是一个 double 的大小(因此在 32 位的系统下,传值需要两个 cycle)。V8 是用 tagged pointer,总是一个指针的大小,浮点数存在堆上。MaybeObject 还是很多非 JS 语言对象(代码块等等)的父类
 ObjectImpl JSObject JS 对象(obj instanceof Object)的实现类。SpiderMonkey 与 V8 皆是把部分属性存放在对象行内,部分用外链的一个数组储存的模式。SpiderMonkey 也将小数组的数据存在行内(但是不会分两半),而 V8 的 JS 数组对象(貌似)都是将数据存在外链的数组上。在 SpiderMonkey 中,只有在 new space 的 JS 对象的外链数组的空间由虚拟机分配,其他情况的外链数组的空间malloc 分配(因此内存会比较分裂?)。ObjectImpl 除了有连接到 Shape 的指针之外,还有一个链到 TypeObject 的指针:16 B(ObjectImpl)vs. 12 B(JSObject)。在 SpiderMonkey 中,数据用 Value 存,也就是说,对于一个有 k 个行内属性的 JS 对象来说,内存占用是:16 B + k * 8 B(ObjectImpl)vs. 12 B + k * 4 B(JSObject),行外的情形 V8 则会再多用 8 B(FixedArray 的标头)。
ShapeTypeObjectClass Map 隐藏类的实现类。在 SpiderMonkey 中,属性名存在 Shape 上(因此带有 V8 的 DescriptorArray 的作用),JS 对象的原型存在 TypeObject 上,特殊属性的处理方法存在 Class 上(Class 对象不是 JS 堆里的对象,大部分都共用:JSObject::class_JSFunction::class_……)。由于 V8 的 Map 有 SM 三个类的作用,因此占用内存也比较多:24 B(Shape)vs. 40 B(Map)。

内存布局与对象创建

运行时

SpiderMonkey V8 备注
Interpret (无) 解释器的主回圈。V8 没有字节码与解释器。

堆与 GC

SpiderMonkey V8 备注
Nursery NewSpace  

调优

一个容易调优的 JS 引擎应该具备有以下条件(从重要的到不重要的):

  • 能透过 profiler 够找到应用的热点函数(当前内联过的函数是怎么处理的?)。
  • 调整各个常量方便。
  • 能(透过内置函数扩展等等)得到以下影响性能的数据:
    • 内联缓存(inline cache)中缓存的隐藏类个数。
    • 函数运行状态(哪个阶段的 JIT?)与机械码。
    • 一个 JS 函数已内联的函数。
    • 一个 JS 函数调用运行时 C++ 函数的个数。
    • 一段时间中 CPU 运行的指令个数与种类(SpiderMonkey 里有个 PerfMeasurement,这个好用么?)。
  • 能得到 bailout 的原因。
  • 代码易读,容易调试。

参见

跟 “JS 引擎” 比较无关,但是中文的 JS 引擎相关的博文也比较少,这里搜集一下:

代码美观

这件事实在不应该是决定使用哪个 JS 引擎的因素,不过……

如果(不幸的)需要以 SpiderMonkey 作为基础调优则会碰到以下问题:

  1. C 与 C++ 代码混用。SpiderMonkey 原先是 C 写成的,后来大规模的以 C++ 改进,但是留下了很多残骸:宏、不是宏但是名称是大写的函数。(参见 JS::Value 的说明。)
  2. 成员变量、方法命名缺乏规则。ObjectImpl::slotsObjectImpl::shape_ 同时存在(前面那个应该加底线)。这个或许是可以修复的 bug。
  3. 混入 JavaScriptCore、V8 代码。SpiderMonkey 的组译器是 JavaScriptCore 的,那些类的成员变量是 m_buffer 等等,又增加了成员变量命名的。另外还有 WTF_* 宏,真是 WTF。再批。妈的,WTF_CPU_ARM_THUMB2 跟本不可能为真,这里有大量的死代码(所有代码完全没有用到 JSC::MacroAssembler,用到的是 js::jit::MacroAssembler)。另外,
  4. 混乱的名称空间。
  5. structclass 混用。
  6. 文档命名缺少规则。C 时代的档名是 jsobj.h,C++ 时代的档名是 Value.h 。请自重。
  7. 过渡滥用宏。

文档标签和贡献者

 此页面的贡献者: ziyunfei, Kennyluck
 最后编辑者: ziyunfei,