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. UITextField和UIViewConteoller

    UITextField控件 UITextFiled常用属性和方法   UITextField是常用的文本输入控件,比如我们用的QQ的登录界面,词典输入要查询的单词都使用了文本框控件,如下图所示.之前介 ...

  2. windows MySQL 5.7 导出表方法记录

    首先修改MySQL配置 找到mysql安装目录下的 my.ini 文件,将 secure-file-priv = "C:/ProgramData/MySQL/MySQL Server 5.7 ...

  3. NOIP模拟赛---1.生气的LJJ (anger)

    LJJ刚上完了一节课!这节课是数学课!他知道了加减属于一级运算,乘除属于二级运算,幂则属于三级运算,而幂的优先级>乘除的优先级>加减的优先级(这是几年级的数学课).但是,从上一套试卷+上一 ...

  4. POJ3352 Road Construction 双连通分量+缩点

    Road Construction Description It's almost summer time, and that means that it's almost summer constr ...

  5. html中的空白字符问题

    1.当我们想使用百分比来进行两个盒子的并排 代码: <!DOCTYPE html> <html lang="en"> <head> <me ...

  6. PHP 多线程、多进程

    多线程:PHP其实并不支持多线程,只是通过一些扩展或者socket方式伪装成多线程,实质不是的.在PHP 5.3 以上版本,使用 pthreads PHP扩展,可以使PHP真正地支持多线程:或者使用 ...

  7. jQuery扩展两类函数(对象调用,静态调用)

    作者:zccst 先看小例子: $(function(){ //扩展方式1-通过对新调用 $.fn.each1=function(){ console.log("hehehehe$.fn.f ...

  8. STM32音乐播放器,文件查找的实现

    使用FATFS只是完成了一个基本的文件读写,有时候我们需要扩展一些功能,比如MP3实验,需要上一曲下一曲的切换,扩展的代码如下 //显示目录下所有文件 u8 ShowFileList(u8* dirP ...

  9. IOS开发-OC学习-常用功能代码片段整理

    IOS开发-OC学习-常用功能代码片段整理 IOS开发中会频繁用到一些代码段,用来实现一些固定的功能.比如在文本框中输入完后要让键盘收回,这个需要用一个简单的让文本框失去第一响应者的身份来完成.或者是 ...

  10. PHP的高并发和大数据处理

    收集前人的经验.加速学习,解决工作中的难题. 一.代码优化(包括sql语句的优化), 合理的使用索引,避免整表查询.二.日常海量数据处理我用文件缓存,文件缓存分两种,第一种是最常见的生成html静太文 ...