Netscape Navigator 在 90 在年代中期对 JavaScript 进行了集成,这让网页开发人员对 HTML 页面中诸如 form 、frame 和 image 之类的元素的访问变得非常容易。由此 JavaScript 很快成为了用于定制控件和添加动画的工具,到 90 年代后期的时候,大部分的 JavaScript 脚本仅仅完成像“根据用户的鼠标动作把一幅图换成另一幅图”这样简单的功能。

随着最近 AJAX 技术的兴起,JavaScript 现在已经变成了实现基于 web 的应用程序(例如我们自己的 Gmail)的核心技术。JavaScript 程序从聊聊几行变成数百 KB 的代码。JavaScript 被设计于完成一些特定的任务,虽然 JavaScript 在做这些事情的时候通常都很高效,但是性能已经逐渐成为进一步用 JavaScript 开发复杂的基于 web 的应用程序的瓶颈。

V8 是一个全新的 JavaScript 引擎,它在设计之初就以高效地执行大型的 JavaScript 应用程序为目的。在一些性能测试中,V8 比 Internet Explorer 的 JScript 、Firefox 中的 SpiderMonkey 以及 Safari 中的 JavaScriptCore 要快上数倍。如果你的 web 程序的瓶颈在于 JavaScript 的运行效率,用 V8 代替你现在的 JavaScript 引擎很可能可以提升你的程序的运行效率。具体会有多大的性能提升依赖于程序执行了多少 JavaScript 代码以及这些代码本身的性质。比如,如果你的程序中的函数会被反复执行很多遍的话,性能提升通常会比较大,反过来,如果代码中有很多不同的函数并且都只会被调用一次左右,那么性能提升就不会那么明显了。其中的原因在你读过这份文档余下的部分之后就会明白了。

快速属性访问

JavaScript 是一门动态语言,属性可以在运行时添加到或从对象中删除。这意味着对象的属性经常会发生变化。大部分 JavaScript 引擎都使用一个类似于字典的数据结构来存储对象的属性,这样每次访问对象的属性都需要进行一次动态的字典查找来获取属性在内存中的位置。这种实现方式让 JavaScript 中属性的访问比诸如 Java 和 Smalltalk 这样的语言中的成员变量的访问慢了许多。成员变量在内存中的位置离对象的地址的距离是固定的,这个偏移量由编译器在编译的时候根据对象的类的定义决定下来。因此对成员变量的访问只是一个简单的内存读取或写入的操作,通常只需要一条指令即可。

为了减少 JavaScript 中访问属性所花的时间,V8 采用了和动态查找完全不同的技术来实现属性的访问:动态地为对象创建隐藏类。这并不是什么新的想法,基于原型的编程语言 Self 就用 map 来实现了类似的功能(参见 An Efficient Implementation of Self, a Dynamically-Typed Object-Oriented Language Based on Prototypes )。在 V8 里,当一个新的属性被添加到对象中时,对象所对应的隐藏类会随之改变。

下面我们用一个简单的 JavaScript 函数来加以说明:

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

当 new Point(x, y) 执行的时候,一个新的 Point 对象会被创建出来。如果这是 Point 对象第一次被创建,V8 会为它初始化一个隐藏类,不妨称作 C0。因为这个对象还没有定义任何属性,所以这个初始类是一个空类。到这个时候为止,对象 Point 的隐藏类是 C0

执行函数 Point 中的第一条语句(this.x = x;)会为对象 Point 创建一个新的属性 x。此时,V8 会:

  • 在 C0 的基础上创建另一个隐藏类 C1,并将属性 x 的信息添加到 C1 中:这个属性的值会被存储在距 Point 对象的偏移量为 0 的地方。
  • 在 C0 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 x 之后能够找到 C1 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C1

执行函数 Point 中的第二条语句(this.y = y;)会添加一个新的属性 y 到对象 Point 中。同理,此时 V8 会:

  • 在 C1 的基础上创建另一个隐藏类 C2,并在 C2 中添加关于属性 y 的信息:这个属性将被存储在内存中离 Point 对象的偏移量为 1 的地方。
  • 在 C1 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 y 之后能够找到 C2 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C2

咋一看似乎每次添加一个属性都创建一个新的隐藏类非常低效。实际上,利用类转移信息,隐藏类可以被重用。下次创建一个 Point 对象的时候,就可以直接共享由最初那个 Point 对象所创建出来的隐藏类。例如,如果又一个 Point 对象被创建出来了:

  • 一开始 Point 对象没有任何属性,它的隐藏类将会被设置为 C0
  • 当属性 x 被添加到对象中的时候,V8 通过 C0 到 C1 的类转移信息将对象的隐藏类更新为 C1 ,并直接将 x 的属性值写入到由 C1 所指定的位置(偏移量 0)。
  • 当属性 y 被添加到对象中的时候,V8 又通过 C1 到 C2 的类转移信息将对象的隐藏类更新为 C2,并直接将 y 的属性值写入到由 C2 所指定的位置(偏移量 1)。

尽管 JavaScript 比通常的面向对象的编程语言都要更加动态一些,然而大部分的 JavaScript 程序都会表现出像上述描述的那样的运行时高度结构重用的行为特征来。使用隐藏类主要有两个好处:属性访问不再需要动态字典查找了;为 V8 使用经典的基于类的优化和内联缓存技术创造了条件。关于内联缓存的更多信息可以参考 Efficient Implementation of the Smalltalk-80 System 这篇论文。

动态机器码生成

V8 在第一次执行 JavaScript 代码的时候会将其直接编译为本地机器码,而不是使用中间字节码的形式,因此也没有解释器的存在。属性访问由内联缓存代码来完成,这些代码通常会在运行时由 V8 修改为合适的机器指令。

在第一次执行到访问某个对象的属性的代码时,V8 会找出对象当前的隐藏类。同时,V8 会假设在相同代码段里的其他所有对象的属性访问都由这个隐藏类进行描述,并修改相应的内联代码让他们直接使用这个隐藏类。当 V8 预测正确的时候,属性值的存取仅需一条指令即可完成。如果预测失败了,V8 会再次修改内联代码并移除刚才加入的内联优化。

例如,访问一个 Point 对象的 x 属性的代码如下:

point.x

在 V8 中,对应生成的机器码如下:

; ebx = the point object
cmp [ebx, <hidden class offset>], <cached hidden class>
jne <inline cache miss>
mov eax, [ebx, <cached x offset>]

如果对象的隐藏类和缓存的隐藏类不一样,执行会跳转到 V8 运行系统中处理内联缓存预测失败的地方,在那里原来的内联代码会被修改以移除相应的内联缓存优化。如果预测成功了,属性 x 的值会被直接读出来。

当有许多对象共享同一个隐藏类的时候,这样的实现方式下属性的访问速度可以接近大多数动态语言。使用内联缓存代码和隐藏类实现属性访问的方式和动态代码生成和优化的方式结合起来,让大部分 JavaScript 代码的运行效率得以大幅提升。

高效的垃圾收集

V8 会自动回收不再被对象使用的内存,这个过程通常被称为“垃圾收集(Garbage Collection)”。为了保证快速的对象分配和缩短由垃圾收集造成的停顿,并杜绝内存碎片,V8 使用了一个 stop-the-world, generational, accurate 的垃圾收集器,换句话说,V8 的垃圾收集器:

  • 在执行垃圾回收的时候会中断程序的执行。
  • 大部分情况下,每个垃圾收集周期只处理整个对象堆的一部分,这让程序中断造成的影响得以减轻。
  • 总是知道内存中所有的对象和指针所在的位置,这避免了非 accurate 的垃圾收集器中普遍存在的由于错误地把对象当作指针而造成的内存溢出的情况。

在 V8 中,对象堆被分成两部分:用于为新创建的对象分配空间的部分和用于存放在垃圾收集周期中生存下来的那些老的对象的部分。如果一个对象在垃圾收集的过程中被移动了,V8 会更新所有指向这个对象的指针到新的地址。

V8 Javascript 引擎设计理念的更多相关文章

  1. V8 javascript 引擎

    V8是一个由丹麦Google开发的开源java script引擎,用于Google Chrome中.[2]Lars Bak是这个项目的组长.[3]   V8在执行之前将java script编译成了机 ...

  2. V8:V8(Javascript引擎)

    ylbtech-V8:V8(Javascript引擎) Lars Bak是这个项目的组长,目前该JavaScript引擎已用于其它项目的开发.第一个版本随着第一个版本的Chrome于2008年9月2日 ...

  3. Chrome浏览器的版本查看 以及V8 javascript 引擎版本查看

    1. 发现chrome浏览器最新版本里面带的V8 引擎 版本号与chrome的版本号有一个关系, 这里简单总结一下: 在地址栏里面输入: chrome://version 即可显示出来 比如我正在使用 ...

  4. windows下使用VS2015编译V8 JavaScript引擎(v5.5 - 2016/09)

    今天心血来潮, 下载了 v8,,然后就想着用vs编译 但是大家都苦恼的是 v8并不直接提供 vs用的项目文件和解决方案(.sln) 于是,在网上搜来搜去, 折腾来折腾去的; 终于一点一点的尝试, 可以 ...

  5. [原创]Windows下Google V8 javascript引擎编译

    项目用到将v8嵌入到C++的情况,公司没时间研究,只有在家研究,编译过程一堆坑.记录一下. 网上百度的都是基于vs2010,或者早版本的v8编译,最新版本应该使用vs2013\vs2015.本文介绍的 ...

  6. JavaScript 引擎 V8 执行流程概述

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/t__Jqzg1rbTlsCHXKMwh6A作者:赖勇高 本文主要讲解的是V8的技术,是V8的入 ...

  7. v8引擎详解(摘)-- V8引擎是一个JavaScript引擎实现

    随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScript脚本.V8引擎就是为解决这一问题而生,在node中也 ...

  8. JavaScript 引擎「V8」发布 8.0 版本,内存占用量大幅下降

    上周,JavaScript 引擎「V8」的开发团队在该项目官方网站上正式宣布推出最新的 8.0 版本.这次更新的重点主要集中在错误修复及性能改善上,正式的版本将在数周后随着谷歌 Chrome 80 稳 ...

  9. 浏览器-09 javascript引擎和Chromium网络栈

    语言的运行 C/C++语言 使用编译器直接将它们编译成本地代码(机器指令),这是由开发人员在代码编写完成之后实施; 用户只是使用这些编译好的本地代码,这些本地代码被系统的加载器加载执行,由操作系统调度 ...

随机推荐

  1. mytop

    http://jeremy.zawodny.com/mysql/mytop/mytop.html 内容有点过时,用?查看帮助 vi set nuset nonu总记不住

  2. php命名空间如何引入一个变量类名?

    例子如下: $className = 'Test'; 正常 new \Require\allClass\Test(); 当然OK,但是 new \Require\allClass\$className ...

  3. Keepalived详细介绍简介

    1.1.Keepalived简介 Keepalived是Linux下一个轻量级别的高可用解决方案.高可用(High Avalilability,HA),其实两种不同的含义:广义来讲,是指整个系统的高可 ...

  4. jQuery常用的查找Dom元素方法

    废话不多说,先来个总结,然后下面是demo 一. 同级节点之间的检索(检索深度N=0) next()是在兄弟节点中,往后匹配; prev()是在兄弟节点中,往前匹配. 二. 父级/子级节点的检索(检索 ...

  5. OpenStack - liberty CentOS 7

    OpenStack私有云部署 Controller Node:       em1(10.6.17.11),em2() Computer Node:         em1(10.6.17.12),e ...

  6. iOS8学习笔记-构建多视图应用程序

    严格来说,凡是UIView的子类都可以称为视图,实际上IOS的图形界面就是视图包含视图的方案,绝大多数的控件都是UIView的子类,但是苹果公司定义的视图术语,通常是指具有相应视图控制器的uiview ...

  7. Android中SQLite的使用

    SQLite是Android中内置的数据库,SQLite是轻量级数据库,支持标准的SQL语法,并且支持ACID事物. 在Android中提供了SQLIteOPenHelper类,帮助我们使用SQLit ...

  8. Keil STM32调试,使用ST-Link下载程序时提示“flash timeout.reset the target and try it again”

    参考: 很郁闷,买来没多久的 STM32F4-DISCOVERY 就挂了? STM32F103RB   Jlink调试的时候出现flash timeout.reset the target and t ...

  9. IOS开发-UI学习-UITextField的具体属性及用法

    直接上代码,里面有各种属性的用法注释,至于每个属性有多个可以设置的值,每个值的效果如何,可以通过查看这个函数参数的枚举量,并逐一测试. //制作登陆界面 #import "ViewContr ...

  10. Struts2---Result(传统Web应用程序与Ajax应用程序的异同)

    看了很久的struts,在视频和书的引导下,慢慢明白了点,推荐:<struts 2 in action>和马士兵的视频 今天看结果这块时,由于还没有学过ajax等,不太明白,但是必须弄懂嗒 ...