深入理解 JavaScript 的 V8 引擎(1)

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

前言

作为 web 工程师,可能我们每天都在和 javascript 打交道 ,但是可能还不了解这些 javascript 代码是如何在浏览器上进行运行起来。

008.png

这个就是今天我们要讨论的问题,以 V8 引擎为例,了解一下 javascript 是如何在 V8 引擎上运行的。可以有的朋友会说 V8 引擎离我们太遥远了,了解 V8 引擎内部的结构还是有助于我们写出高性能的代码。

006.png

网上有很多关于 V8 引擎的分享,通过阅读自己也是受益匪浅。所以也想从自己角度来大家一起再次领略一下 V8 的美。

010.jpeg

什么是 js 引擎以及当下流行的引擎

像 javascript 这样编程语言,都是对我们程序员友好的,便于我们将一些流程和逻辑用 javascript 语言描述出来。

在像 javascript 这样语言设计都是为了便于程序员开发应用程序,但是机器并不了解 javascript 的语法,机器更熟悉的 cpu 的指令集,所以就需要将程序员熟悉的 javascript 编译成机器熟悉的机器码来执行程序,下面列出一些当下流行 javascript 引擎,不过今天我们主角是 V8

011.png

  • Chrome V8
  • Firefox SpiderMonkey
  • Safari JavaScriptCore
  • Edge Chakra

V8 引擎

今天我们讨论的主角是 V8 引擎有由 Google 开源高性能 Javascript 和 WebAssembly 引擎,使用 c++ 编写的,用于 Chrome 和 Nodejs。其中chrome的市场占有率已经达到 70%。以下 V8 引擎一些新特征。

009.png

  • 多线程程序
  • 用 cpp 开发的程序
  • 将 javascript 代码转换并优化为机器码
  • 支持 JIT(just in time) 编译
  • node 也是运行 V8 引擎上

015.jpeg

老版本的 V8 架构以及其存在的问题

我们先看一下早期的 V8 引擎的结构以及其存在问题,早期 V8 引擎有 Full-Codegen 和 Crankshaft 两个编译器。V8 首先用 Full-Codegen 把所有的代码都编译一次,生成基准机器码。JS 在执行的过程中,V8 内置的 Profiler 分析器筛选出热点函数并且记录参数的反馈类型,然后解析再次生成 AST 交给 Crankshaft 来进行优化。所以 Full-Codegen 本质上是生成的是未优化的机器码,而 Crankshaft 生成的是优化过的机器码。

  • javascript 会被解析两次,第一次用于 Full-Codegen 生成机器码,而第二次用于 Crankshaft 进行优化
  • Full-Codegen 生成机器码会占用大量内存,一个 1M 左右 的 js 源码的文件,编译生成的二进制代码可能就是十几 M,而早期手机的内存普遍不高,过度占用会导致性能大大降低
  • 而且为了兼容不同架构 CPU Full-Codegen引擎以及优化编译的Crankshaft引擎要针对不同的CPU架构编写代码

新版本的 V8 进行重大改变

在 2017 年时已完全废弃并移除了 full-codegen 和 Crankshaft,v8 的 5.9 版本发布了,新增了一个 Ignition 字节码解释器,将默认启动。Ignition 引擎可以对字节码进行解释执行,那就是说他的功能类似于Java的JVM,本质上就是一个虚拟机。 开始逐句对字节码进行解释成二进制代码并执行。在解释执行的过程中,标记重复执行的热点代码,将标记的代码通过Turbofan引擎进行编译生成效率更高二进制代码,再次运行到这个函数时便只执行高效代码而不再解释执行字节码。

016.jpeg

  • TurboFan 图生成器可以直接利用生成好的字节码,然后在 TurboFan 里面优化函数,这样就避免了重新解析 JavaScript 源代码
  • 字节码引入降低 CPU Full-Codegen引擎以及优化编译的Crankshaft引擎要针对不同的CPU架构编写代码繁重的工作
  • 字节码体积相对于机器码要小得多

这里只是从从整体架构给大家介绍 V8 引擎给大家简单介绍一下,随后会围绕 V8 引擎中一些关键环节和技术根大家一起分享。

012.jpeg

  • 解释器: 是一种直接执行高级语言代码的计算机程序, 而无需将代码编译成机器码
  • 编译器: 把源代码转换成机器码执行
  • 字节码: javascript 高级语言和二进制代码之间的差异是相当大的,直接转换会非常麻烦,这时就有了二者中间的代码,有了字节码我们在这个层面进行优化和兼容不同架构 cpu 的工作