转自http://blog.pluskid.org/?p=186
本文翻译自 Google 的开源 Javascript 引擎 V8 的在线文档。其实我都没有真正翻译过什么东西,本来我的英文就比较一般,中文语言组织也很弱。而且许多文档(比如这篇)基本上如果是对此感兴趣的人,直接阅读英文原文文档肯定都是没有问题的。不过既然突然心血来潮,就试一试吧,能力总是要锻炼才会有的。我自己对 Language VM 比较感兴趣,V8 其实并不是一个 VM ,因为它是直接编译为本地机器码执行的,但是也有不少相通的地方。废话少说,下面是译文。
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 代码以及这些代码本身的性质。比如,如果你的程序中的函数会被反复执行很多遍的话,性能提升通常会比较大,反过来,如果代码中有很多不同的函数并且都只会被调用一次左右,那么性能提升就不会那么明显了。其中的原因在你读过这份文档余下的部分之后就会明白了。
V8 的性能提升主要来自三个关键部分:
快速属性访问
动态机器码生成
高效的垃圾收集
快速属性访问
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。
map_trans_a
执行函数 Point 中的第一条语句(this.x = x;)会为对象 Point 创建一个新的属性 x。此时,V8 会:
在 C0 的基础上创建另一个隐藏类 C1,并将属性 x 的信息添加到 C1 中:这个属性的值会被存储在距 Point 对象的偏移量为 0 的地方。
在 C0 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 x 之后能够找到 C1 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C1。
map_trans_b
执行函数 Point 中的第二条语句(this.y = y;)会添加一个新的属性 y 到对象 Point 中。同理,此时 V8 会:
在 C1 的基础上创建另一个隐藏类 C2,并在 C2 中添加关于属性 y 的信息:这个属性将被存储在内存中离 Point 对象的偏移量为 1 的地方。
在 C1 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 y 之后能够找到 C2 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C2。
map_trans_c
咋一看似乎每次添加一个属性都创建一个新的隐藏类非常低效。实际上,利用类转移信息,隐藏类可以被重用。下次创建一个 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引擎内部构造详解 #### 背景与目的 V8 是由 Google 开发的一款开源的、高性能的 JavaScript 引擎。它最初应用于 Google Chrome 浏览器,并逐渐成为众多现代浏览器和 Node.js 的核心组成部分。V8 ...
首先,V8引擎的设计理念是即时编译(JIT,Just-In-Time compilation)。与传统的解释器不同,V8不是逐行解释执行JavaScript代码,而是将代码转换为本地机器码,这使得V8能够达到接近原生应用的运行速度。在V8源代码...
为了解决上述问题,V8最终采用了类似于JavaScriptCore的设计理念,引入了字节码这一中间层。现在的V8架构包括Ignition解释器和TurboFan优化编译器两大部分: - **Ignition**:作为V8的新解释器,其主要目的是为了...
Node.js是一种基于Chrome V8 JavaScript引擎的开源JavaScript运行时环境,它彻底改变了后端开发的格局。这个平台的出现使得JavaScript不再局限于浏览器环境,而能够用于服务器端编程,实现了全栈开发的可能性。Node....
标题中的“Node.js”是指一个基于Google的V8 JavaScript引擎构建的开源JavaScript运行时环境。这个平台让开发者能够在服务器端使用JavaScript进行编程,打破了JavaScript仅限于浏览器客户端使用的传统格局。Node.js...
本文将基于“Chrome-V8.pdf”文档提供的信息,深入探讨V8的设计理念、关键技术以及其实现细节。 #### V8的关键特性 - **速度与可扩展性**:V8引擎设计之初就旨在提高JavaScript的执行效率,确保其能够应对日益复杂...
### Node.js:基于Chrome V8引擎的JavaScript运行时环境 #### 一、概述 Node.js是一种开放源代码、跨平台的JavaScript运行环境,它能够使开发者使用JavaScript编写服务器端应用程序。Node.js的设计理念是实现非...
本文将重点介绍artTemplate——一种高性能的JavaScript模板引擎,并深入探讨其设计理念、关键技术以及应用场景。 #### 二、artTemplate概述 ##### 2.1 引擎特点 artTemplate是一种新一代的JavaScript模板引擎,其...
虽然名字相似,但JavaScript与Java在语法和设计理念上有着显著差异。 在JavaScript程序设计中,你需要了解以下几个关键知识点: 1. **基础语法**:JavaScript是一种基于原型的对象导向语言,具有动态类型。变量...
综上所述,FuzzIL作为一种专为JavaScript引擎设计的指导性模糊测试框架,在提高漏洞检测效率方面展现出了巨大潜力。通过结合语义变异和动态覆盖指导技术,FuzzIL能够有效探测JavaScript引擎中的深层次漏洞,对于提高...
这一理念源于Google的V8 JavaScript引擎,它为Node.js提供了强大的性能基础。Node.js的设计使得它能够处理大量并发连接,尤其适合实时、数据密集型的网络应用,如实时聊天、协作工具、流媒体服务等。 **V8引擎** ...
尽管名字相似,JavaScript与Java在语法和设计理念上有着显著的差异。 **JavaScript简介** JavaScript是一种轻量级、解释型的编程语言,主要用于客户端的网页交互,它允许开发者通过DOM(Document Object Model)来...
总结起来,Deno作为一个基于V8和Go理念的安全TypeScript运行时,它提供了一个更现代化、安全的JavaScript开发环境,旨在改进Node.js的一些设计决策,以满足开发者对效率、安全和模块化的需求。随着社区的不断壮大和...
Node.js的核心设计理念是事件驱动编程。在Node.js中,当一个I/O操作完成时,不会立即返回结果,而是会触发一个事件,等待事件循环系统来处理。这种方式提高了程序的并行处理能力,减少了CPU的空闲时间。 2. **非...
虽然名字中带有“Java”,但JavaScript与Java并无直接关系,它们各自遵循不同的语法和设计理念。JavaScript主要运行在浏览器环境中,通过DOM(文档对象模型)与HTML和CSS进行交互,实现了网页的动态化。 二、...
至于"占用资源少",这与框架的设计理念和优化策略有关。比如,上述的Express和Koa都强调轻量级,它们不捆绑任何库或中间件,开发者可以根据需要选择添加,避免了不必要的资源消耗。此外,一些框架如Next.js和Nuxt.js...
JavaScript并非只有一种形态,它有多种解释器,如浏览器中的JavaScript引擎(如Chrome的V8、Firefox的SpiderMonkey),还有Node.js用于服务器端开发,以及各种框架和库如React、Vue、Angular等。理解这些不同的环境...
JavaScript代码解释执行,由浏览器的JavaScript引擎负责解析和运行,如Chrome的V8引擎。 JavaScript有多个版本,从最初的JavaScript1.0到后续的版本,随着技术的发展,标准也在不断升级。现代浏览器通常支持最新的...
尽管名字相似,JavaScript与Sun Microsystems的Java语言在语法和设计理念上存在显著差异。 JavaScript主要应用于客户端的网页交互,允许开发者创建动态、响应式的网页内容。它能够操作DOM(Document Object Model)...
它是由Netscape公司的Brendan Eich在1995年发明的,最初被命名为LiveScript,后因与Sun Microsystems的Java语言合作推广而更名为JavaScript,但两者在语法和设计理念上存在显著差异。 JavaScript的主要用途是增强...