最近开始学习V8 Javascript引擎,这篇文章是翻译官方文档的,解释了V8之因此快的主要缘由等,原文请参见http://code.google.com/apis/v8/design.html。 html
V8是一个新的为了提升Javascript程序速度的Javascript引擎,在多项测试中,V8的速度比JScript (in Internet Explorer), SpiderMonkey (in Firefox), and JavaScriptCore (in Safari)都要快不少倍,若是你的JS网络应用程序正受制于JS引擎的速度,那么使用V8而不是你如今使用的JS引擎将能够大幅改善你的应用程序的表现。至于提高的幅度则由JS所占的比例以及JS的结构(nature of JS)等多方面因素决定,例如,若是一个函数在你的应用中将会被一次次重复运行,那么提高的幅度将会比不少函数都在你的应用中只运行一次要大不少,至于会这样的缘由将会在后面的文章中解释。 编程
V8速度提高的三个主要方面为: api
JS是一种动态语言,对象的属性(property)能够在运行时被动态的添加和删除,这意味着对象的属性颇有可能被改变,绝大多数JS引擎采用一个字典型的数据结构来存储对象的属性,每一次属性的访问都须要一次动态的查询以得到属性在内存中的位置,这样的访问方法使得JS中的属性访问要比普通编程语言例如Java和Smalltalk中的对象访问要慢不少,在这些(普通的)编程语言中,对象的属性值根据类的固定结构,被编译器放在离对象指针有固定的偏移值的内存位置上,属性的访问只是一次简单的内存读取和写入,一般只须要一个(汇编)指令。 缓存
为了减小访问JS属性的时间,V8没有采用动态查询的方式,V8会在背后动态的建立隐藏类,在V8中,当对象的属性改变时,对象会更改隐藏类的指向。这种方式的基本思想并非被创新出来的,在prototype-based的编程语言Self中也作了相似的事情,详见An Efficient Implementation of Self, a Dynamically-Typed Object-Oriented Language Based on Prototypes。 网络
咱们经过举例来更加清楚的了解整个过程。请看下面这个很简单的JS函数。 数据结构
function Point(x, y) { this.x = x; this.y = y; }
当new Point(x, y)被执行的时候,一个新的Point对象会被建立,当V8第一次执行这个函数的时候,V8会为Point建立一个初始的隐藏类,为方便,咱们假设这个类叫C0,显然这个类里面什么都没有,这个时候,Point这个对象的隐藏类是C0。 app
当执行Point中的第一个语句(this.x = x;),会在Point对象中建立一个新的属性x,对于V8而言,会作以下事情: 编程语言
当执行Point中的第二个语句(this.y = y;)的时候,将会在Point对象中建立一个新的属性y,对于V8而言,会作以下事情: ide
每次有一个属性被添加的时候就要从新建立一个隐藏类的作法看起来好像很低效,可是,由于类转移的存在,隐藏类能够被复用(如我上面所解释)。尽管JS比其余面向对象的编程语言都要更加动态,可是采用上面的方法,经过观察不少JS程序的运行,它们在很大程度上都重用了以前的结构(the runtime behavior of most JS programs results in a high degree of structure-sharing using the above approach). 利用隐藏的方法有两个好处:属性的访问再也不须要一个字典查询,同时可让V8使用一些在以类为基础的普通编程语言中能够用的优化方法,例如inline caching(参考 Efficient Implementation of the Smalltalk-80 System)。 函数
V8会JS代码第一次运行的时候将其直接编译为机器码,在V8中没有中间字节码,也就没有解释器,属性的访问由inline cache来完成,但这些代码可能会在V8执行期间被更改成别的机器指令。
当第一次执行访问某个对象的某个属性的代码的时候,V8决定这个对象如今的隐藏类,同时会进行以下优化,V8假设当前代码块中的对这个对象的全部的属性访问都会使用这个隐藏类,并根据这个假设修改inline cache的代码,直接使用这个隐藏类(跳过查询隐藏类的步骤),若是V8的假设是正确的,那么属性值的读取和赋值只需一个指令便可完成,若是假设错误,那么V8再次修改代码,移除这一优化。
举例,JS中访问一个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运行系统中处理inline cache失败的地方并修改inline cache的代码,若是找到了这个属性,那么x的值就会直接被获得。
当有许多对象共享同一个隐藏类的时候,这样的方法可以使得JS的属性访问速度和大部分静态语言的访问速度相仿,使用隐藏类并经过inline cache代码来访问属性,同时优化机器码的生成,V8可以大幅优化大部分JS代码的执行效率。
V8会将再也不被引用的内存进行回收,这个过程一般被称之为垃圾回收,为了保证快速的对象生成,缩短垃圾回收所形成的暂停,而且防止内存碎片的产生,V8的垃圾回收器使用了以下原则:stop-the-world, generational, accurate。具体来讲:
在V8中,对象堆内存被分为两部分,用于新对象建立的新的内存空间,用于存放在垃圾回收周期中存留下来的对象,若是一个对象在垃圾回收中被移动了,V8会更新全部指向改对象的指针。