0
点赞
收藏
分享

微信扫一扫

浏览器原理之V8引擎

穆风1818 2022-05-01 阅读 56

V8 之 what & why & when?

waht:V8是一个由Google开源的采用C++编写的高性能JavaScriptWebAssembly引擎,应用在 ChromeNode.js等中。它实现了ECMAScriptWebAssembly,运行在Windows 7及以上、macOS 10.12+以及使用x64、IA-32、ARMMIPS处理器的Linux系统上。 V8可以独立运行,也可以嵌入到任何C++应用程序中。

when:V8最初是由Lars Bak团队开发的,以汽车的V8发动机(有八个气缸的V型发动机)进行命名,预示着这将是一款性能极高的JavaScript引擎,在2008年9月2号chrome一同开源发布。

why:JavaScript代码最终要在机器中被执行,需要经过一系列的处理,将高级语言转换成二进制码指令,机器才可以识别和执行。而转换过程由 V8 负责完成

V8的组成部分

V8的内部有很多模块,其中最重要的4个如下:

  • Parser: 解析器,负责将源代码解析成AST;
  • Ignition: 解释器,负责将AST转换成字节码并执行,同时会标记热点代码;
  • TurboFan: 编译器,负责将热点代码编译成机器码并执行;
  • Orinoco: 垃圾回收器,负责进行内存空间回收;

以下是 V8 中这几个重要模块的具体工作流程图。接下来我们除垃圾回收器 Orinoco 之外逐个进行分析。

Parser 解析器

Parser解析器的转换过程有两个重要阶段:词法分析(Lexical Analysis)语法分析(Syntax Analysis)。

词法分析阶段是扫描输入的源代码字符串,生成一系列的词法单元 (tokens),这些词法单元包括数字,标点符号,运算符等。词法单元之间都是独立,该阶段并不关心代码的组合方式。

JavaScript中的token主要包含以下几种:

比如, const name = 'zedran'经过esprima词法分析后生成的tokens:

[
    {
        "type": "Keyword",
        "value": "const"
    },
    {
        "type": "Identifier",
        "value": "name"
    },
    {
        "type": "Punctuator",
        "value": "="
    },
    {
        "type": "String",
        "value": "'zedran'"
    }
]

语法分析阶段是将词法分析产生的token按照某种给定的形式文法转换成AST的过程。也就是把单词组合成句子的过程。在转换过程中会验证语法,语法如果有错的话,会抛出语法错误。 

上述const name = 'zedran'经过语法分析后生成的AST如下:

{
  "type": "Program",
  "start": 0,
  "end": 21,
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 0,
      "end": 21,
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 6,
          "end": 21,
          "id": {
            "type": "Identifier",
            "start": 6,
            "end": 10,
            "name": "name"
          },
          "init": {
            "type": "Literal",
            "start": 13,
            "end": 21,
            "value": "zedran",
            "raw": "'zedran'"
          }
        }
      ],
      "kind": "const"
    }
  ],
  "sourceType": "module"
}

此后,经过Parser解析器生成的AST将交由Ignition解释器进行处理。

Ignition 解释器

Ignition 解释器负责AST转换成字节码(Bytecode)并执行。字节码是介于AST和机器码之间的一种代码,与特定类型的机器代码无关,需要通过解释器转换成机器码才可以执行。

聪明的你可能会问:为何不直接将AST转换成机器码直接运行呢?

Ignition解释器的工作流程图:

 

AST需要先通过字节码生成器,再经过一系列的优化之后才能生成字节码。 其中的优化包括:

  • Register Optimizer:主要是避免寄存器不必要的加载和存储
  • Peephole Optimizer:寻找字节码中可以复用的部分,并进行合并
  • Dead-code Elimination: 删除无用的代码,减少字节码的大小

Ignition 解释器在执行字节码的过程中,会监视代码的执行情况并记录执行信息,如函数的执行次数、每次执行函数时所传的参数等。被执行多次的同一段代码,会被标记成热点代码,交给TurboFan编译器进行处理。

TurboFan 编译器

TurboFan 编译器拿到 Ignition 解释器标记的热点代码后,会先优先将优化后字节码编译成更高效的机器码存储起来。下次再次执行相同代码时,会直接执行相应的机器码,大大提升了代码的执行效率。

当一段代码不再是热点代码后,TurboFan会将优化编译后的机器码还原成字节码,将代码的执行权利交还给 Ignition 解释器

现在我们来看一看具体的执行过程:以多次执行 sum = sum + arr[i] 为例,JS是动态类型的语言,每次的 sum 和 arr[i] 都有可能是不同的类型,在执行这段代码时,Ignition 每次都会检查 sum 和 arr[i] 的数据类型。其被标记为热点代码,交给 TurboFan 编译器直接以之前的几次执行确定 sum 和 arr[i] 的数据类型编译成机器码。

但如果在后续的执行过程中,arr[i] 的数据类型发生了改变,之前生成的机器码就不满足要求了,TurboFan 编译器会把之前生成的机器码丢弃,将执行权利再交给  Ignition 解释器,完成去优化的过程。

总结

V8 的执行过程:

  1. 源代码经过 Parser 解析器,经过词法分析和语法分析生成 AST;
  2. AST经过 Ignition 解释器生成字节码并执行;
  3. 在执行过程中,如果发现热点代码,将热点代码交给 TurboFan 编译器生成机器码并执行;
  4. 如果热点代码不再满足要求,进行去优化处理。
举报

相关推荐

0 条评论