V8引擎执行JavaScript代码过程
提问: V8引擎做了什么?
答: 因为电脑在执行代码的时候是交给CPU来执行的,可是我们编写的JavaScript是属于高级语言,CPU是无法执行高级语言的,需要转换成对应的二进制代码码才能CPU识别,才能运行起来。所以V8引擎做的就是这个过程,并且在这个过程中还进行了优化。
- 1、先将JavaScript代码进行
Parse(解析)成AST(抽象语法树) - 2、将
AST(抽象语法树)'Ignition'转成bytecode(字节码) - 3、将
bytecode(字节码)转成二进制码让CPU能够执行的指令 - 4、V8引擎在字节码转换成二进制码的时候会进行一个
TurboFan过程,将字节码装换成MachineCode(优化后的二进制码),这是V8引擎的一个优化手段。
比如,你写了一个方法并进行了多次调用做同一个操作时,V8引擎会直接将字节码经过一个TurboFan的过程,然后装换成MachineCode(优化的二进制码),之后下次再做同样的操作的时候,就会直接去执行优化后的二进制码,就不需要在去装换成二进制码给CPU执行。如果你有一次做的操作是其他操作,如下面一直是数字相加,后面来了字符串拼接,这时候V8引擎会经行一次Deoptimization(反优化),换成原来对应的二进制码给CPU执行,等到下次再做同样的操作的时候V8引擎又会装换成优化后的二进制码,之后再做同样操作的时候就会让CPU执行优化后的二进制码。
function sum(num1, num2) {
var result = num1 + num2
}
sum(10, 10) // 执行
sum(10, 20) // 优化二进制码执行
sum(10, 30) // 执行优化后的二进制码
sum("ab", "c") // 将优化后的二进制转成原来的二进制码执行
sum("ab", "d") // 优化二进制码执行
sum("ab", "e") // 执行优化后的二进制码
V8引擎的架构
-
Parse模块会将JavaScript装换成AST(抽象语法树),这是因为解释器并不直接认识JavaScript代码。- 如果函数没有被调用,那么是不会被转换成AST的
-
Ignition是一个解释器,会将AST装换成ByteCode(字节码)- 同时会收集TurBoFan优化所需要的信息(比如函数的参数的类型信息,有了类型才能进行真实的运算)
- 如果函数只调用一次,Ignition会解释执行ByteCode
-
TurBoFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码。- 如果一个函数被多次调用,那么就会被标记成
热点函数,那么就会经过TurBoFan装换成优化的机器码,提高代码的执行性能。 - 但是,
机器码实际上也会被还原为ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(如sum函数原阿里执行的是number类型,后来执行变成了string类型),之前优化的机器码并不能正确的处理运算,就会你想的转换成字节码
- 如果一个函数被多次调用,那么就会被标记成
V8引擎解析(官方)
-
Bink: 拿到对应的源码,Bink解析HTML,遇到Sctipt元素,浏览器从服务器上下载对应的JavaScript代码后交给了Bink之后Bink又会将代码交给V8引擎
-
Stream: V8引擎会将对应的代码转成Stream
-
Scanner(扫描器、语法分析器):之后会对代码进行解析,对代码进行词法分析
- tokens(记号化、tokenization): 将一条语句进行
分词、将每个词转换成token(记号化),对每个词进行解析,在添加到AST树中,通过关键字来进行区别
- tokens(记号化、tokenization): 将一条语句进行
-
Parse(语法分析): 在对解析后的tokens进行语法分析,通过解析的关键字生成真正的JS代码,最后生成AST树
-
Ignition: 在通过Ignition转成字节码。