前言
此篇是旧博客迁移的文章之一,但在今天(2022-8-10)对内容进行了大量的补充与修正。为何今天才修改内容?主要是因为最近的精力在学习 python 爬虫,在准备爬虫文章的更新,因此搁置了 V8 引擎。平心而论,V8 引擎系列文章会是我真正意义上的”专栏”,一切源于兴趣使然。当然,Chrome 成为当今浏览器巨头的原因,很大一部分在于 V8 引擎,因此我会尝试去阅读 V8 源码,尽管它有一部分源码由 C++组成,但仍有大部分源码使用了 JavaScript,可能不会将源码阅读完(实际上对我来说也不可能),但这能够进一步去了解 V8,算是我对它的 respect…
文章的时间记为 2022-4-9,实际上我也是从这一天开始了解 V8 引擎,就让时间回溯到那一天开始…
为什么需要 JavaScript 引擎呢?
由于 JavaScript 是高级编程语言,而高级编程语言都是需要转成最终的机器指令来执行的。事实上,我们编写的 JavaScript 代码无论交给浏览器或者 Node 来执行,最后都是需要被CPU执行的。但只有机器语言才能被 CPU 执行, 因此JavaScript 引擎帮助我们将 JavaScript 代码编译成 CPU 指令来执行。
常见的 JavaScript 引擎有哪些?
- SpiderMonkey: 第一款 JavaScript 引擎,由 JavaScript 之父开发
- JavaScriptCore: WebKit 中的 JavaScript 引擎,小程序也在使用该引擎
- V8: Chrome 浏览器的 JavaScript 引擎
浏览器内核和 JavaScript 引擎的关系
以 WebKit 为例子,WebKit 事实上由两部分组成:
- WebCore:负责HTML 解析、布局、渲染等相关工作
- JavaScriptCore:解析、执行 JavaScript 代码
什么是 d8?
d8 是 V8 引擎的一个调试工具,通过 d8 的一些指令,我们可以查看 V8 引擎在执行 JavaScript 代码过程中的各种中间数据,比如作用域、AST、bytecode(字节码)、优化的二进制代码、垃圾回收的状态等
如何在 Windows 上安装 d8?
一般来说,网上没有直接获取 d8 的途径,而是通过编译 V8 的源码来生成 d8。
如何通过编译 V8 的源码构建 d8?
这里我简单概括下,分为三步:下载 V8 源码 -> 生成工程文件 -> 通过相关工具的指令编译 V8 的工程并生成 d8
这里,我在网上找到了其他大佬编译好的 d8:
- Windows 系统 64 位:https://storage.googleapis.com/chromium-v8/official/canary/v8-win64-dbg-8.4.109.zip
- Mac 系统:https://storage.googleapis.com/chromium-v8/official/canary/v8-mac64-dbg-8.4.109.zip
介于我使用的是 Windows 系统,下面我将介绍如何在 Windows 系统下安装 d8:
- 通过上方的链接,下载压缩包,解压压缩包到任何地方都行
- 打开解压出的文件,会发现一个
d8.exe
文件 - 此时,将
d8.exe
所在的文件路径 copy 一下 - 打开高级系统设置,点击环境变量
- 在系统变量中的 path 里添加刚刚 copy 的文件路径
测试是否安装成功:
- 添加成功后,可以检测一下 d8 是否可以使用,在电脑的任何地方,按住 shift 键+鼠标右键,选择启动
PowerShell
窗口 - 输入
d8 --help
命令,若出现很多行即表示可以使用 d8 了
V8 引擎的原理
V8 引擎是由 C++编写的高性能JavaScript和 WebAssembly 引擎,它能实现 ECMAScript 和 WebAssembly,也能独立运行并嵌入到任何 C++应用程序中。
(V8 引擎-过程图)
优化策略
- 函数只声明并且未被调用, 则不会被解析成 AST
- 函数若只被调用一次, bytecode 则直接被 Ignition 解释执行
- 函数若被多次调用, Ignition 会将该函数标记为 hot 函数, 若多次调用且参数都为相同类型, 则会被编译成 MachineCode; 若多次调用并且传入不同类型的参数, 则会被反向优化成 bytecode
过程分段解析
过程一
JavaScript 源代码 -> Parse(解析) -> AST(抽象语法树)
过程二
AST(抽象语法树) -> Ignition(转换器/解释器) -> bytecode(字节码)
AST(抽象语法树) -> Ignition(转换器/解释器) -> TurboFan(编译器)
V8 引擎的架构
-
Parse 模块:
Parse 模块会将 JavaScript 代码转换成抽象语法树,帮助 Ignition 解释器认识 JavaScript 代码
如果函数没有被调用,则不会被转换为抽象语法树
-
Ignition 解释器:
Ignition 解释器会将抽象语法树转换为 bytecode 字节码
同时会收集 TurboFan 优化所需要的信息(比如函数参数的类型信息)
如果函数只调用一次,Ignition 会直接执行解释 bytecode 字节码
-
TurboFan 编译器:
如果一个函数被多次调用,那么就会被 Ignition 标记为 hot 热函数,随后被 TurboFan 转换成优化的机器码,提高性能
当出现类型发生变化时,机器码会被还原成 bytecode, 因为之前的优化机器码并不能正确的处理运算,所以就会被逆向的转化成字节码,降低性能
V8 执行的细节
Parse(解析)阶段
(V8 引擎的解析图-官方)
未完待续…