How JavaScript works - 2 (译)js 工做原理

inside the V8 engine + 5 tips on how to write optimized code

原文连接 : https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e javascript

Foreword 前言

两周前咱们开始了js深刻了解以及它如何的工做原理的研究:咱们认为经过了解js的代码构建和他们的运行机制能够写出更好的代码和app。
前面的博文主要是js引擎概述,运行和堆栈。第二篇会深刻谷歌V8的js引擎部分,咱们也会提供一点点关于如何编写更好的js代码的建议-最好java

Overview 概览

js引擎是一个程序或者执行js代码解析器。js引擎能够实现为一个标准的翻译,或者以某种形式将js代码即时编译成字节码。 下面是流行的js执行引擎清单列表:node

  • v8 - 开源,由google开发,用C++ 编写
  • Rhino — 由火狐管理,开源,彻底用java开发
  • SpiderMonkey
  • JavaScriptCore
  • KJS
  • Chakra
  • Chakra
  • Nashorn
  • JerryScript

为什么创造v8引擎

由google开发V8引擎是开源的,用c++编写。这个引擎用于Google Chrome。不像其余的引擎,当前流行的node.js也是基于V8运行。 c++

v8最初旨在增长js在web浏览器中的性能。为了获取速度,V8将js代码转换成更高校的机器代码而不是翻译js。他将js代码编译成机器代码,在执行的时候是即时编译的,就像大多数js引擎模型同样。主要的区别在于v8不会产生任何字节码或者任何中间代码。

v8有两个编译器

在V8的5.9版本出现以前,v8使用两个编译器:

  • full-codegen -- 一个简单快速的编译器,生成编译简单和相对慢的机器码。
  • Crankshaft -- 一个更复杂的(即时)优化编译器,生成高度优化的代码。

v8引擎内部也是使用多线程的:

  • 主线程作你想要的工做:获取代码,编译代码而后执行。
  • 还有一个单独的线程编译,因此主线成能够继续执行代码,而前者是优化代码
  • 在运行时有一个分析器的线程会告诉咱们哪些方法花费了大量的事件,这样Crankshaf能够去优化它
  • 一些线程用来处理垃圾回收扫描

当开始执行js代码,v8使用full-codegen直接解析js翻译成机器码,没有任何转换。这容许它开始执行机器代码很是快。注意v8不使用中间字节码表示这种方式不须要翻译。web

当你的代码运行一段时间后,解析器线程会汇集足够的数据告诉咱们哪些方法必须优化。 接下来,Crankshaft开始在另外一条线程上优化。它将js抽象语法树(syntax tree )转换成一个叫Hydrogen 的高级静态单元分配表示 (SSA) ,并且尝试去优化 Hydrogen这个图。大多数的优化是在这级完成的。编程

Inlining 代码嵌入

首次优化尽量多的提早嵌入代码。代码嵌入是将使用函数的地方(调用函数的那一行)替换成被调用函数的本体。这个简单的步骤可使接下来的优化更有意义。 浏览器

Hidden class 隐藏类

js是一个基于原型链的语言:它没有类和对象是经过克隆产生的。js也是一个动态编程语言,这意味着在对象实力化之后能够很简单增长或者移除属性。 大多数js解析器使用相似字典同样的结构(基于散列函数) 去储存对象属性值在内存中的地址。这种构造使js在检索对象属性值上比其余相似java,c++等动态语言,花费更大的计算量。在java中,全部的对象属性在编译以前都已经被固定的对象容器决定。并且在运行时不能够动态的添加或者删除属性。(c++的动态类型是另外一个话题)。结果,每个属性的值可以以连续的buffer储存在内存中,在每一个属性值之间有一个偏移量。这个偏移量的长度很容易根据属性类型决定。然而在js中这是不可能的,由于js的属性值能够在运行时改变。 由于使用字典去查找对象属性在内存中的地址的效率很低,V8使用了一个不一样的方法代替:隐藏类。隐藏类与在java等语言中使用的固定对象布局的工做方式相似,除了他在运行时被建立。来让咱们看一下真实的代码:缓存

function Point(x,y){
     this.x = x;
     this.y = y;
 }
 var p1 = new Point(1,2);
复制代码

一旦 new Point(1,2) 被调用,V8会建立一个名为 C0 的隐藏类。 session

此时他尚未被定义任何属性,所以 C0 是空的。 一旦第一排代码被执行 “this.x = x” (在Point函数中),V8会建立一个基于 C0 名为 C1 的第二个隐藏类。 C1 记录的是属性x在内存中的地址(相对于对象指针)。在这个案例中,x 被存储在 offset偏移量为0中,这意味着当浏览一个做为持续的buffer指针对象的时候,第一个偏移量对应属性 ‘x’。若是属性 x被放入制定对象的时候,V8也会用一个 过分的类更新 C0的状态。隐藏类就会从 C0C1。如今指定对象的隐藏类指定 C1

每一次新的属性被添加到对象中,旧的隐藏类就会用一个过渡的路径更新到新的隐藏类上。隐藏类的过渡很重要,由于他们容许隐藏类之间共享相同的方式建立对象。若是两个对象分享一个隐藏类,相同的属性会被添加到这两个对象中。过渡能够确保这两个对象接收到相同的隐藏类和全部的优化代码。

Inline caching 内部缓存

Compilation to machine code 编译机械代码

Ignition and TurboFan

How to write optimized JavaScript 如何优化js代码

Resources

相关文章
相关标签/搜索