浏览器从下载网页到渲染的过程与 V8 引擎介绍 —— 下篇

540 阅读3分钟

最近再次学习了关于浏览器渲染网页的过程,并且新了解到了些关于 Chrome 和 node 中所使用的执行 js 代码的 V8 引擎相关知识,在此做个分享。正文部分总计近 2000 字,故而拆分成上下两篇,上篇 主要介绍浏览器渲染网页的过程,下篇主要介绍 V8 引擎。有不足之处或是任何意见建议,欢迎各位大佬不吝斧正~

V8 引擎

V8 是由 C++ 编写的 js 和 webAssembly 引擎,可用于 Chrome 和 NodeJS 等。注意,V8 就是个名称,而不是说有 V7、V6 等等像手机型号那样有一个系列。Chrome 中,由 V8 引擎,经过一系列步骤,将我们书写的 js 代码(高级语言)最终转换成能在 cpu 运行的机器指令,步骤大致如下:

1. Parser

V8 引擎拿到 js 源码后首先进行编码转换,然后通过 Scanner 进行词法分析(lexical analysis)生成 tokens,再交给 Parser 进行语法分析生成抽象语法树(AST)。至于什么是抽象语法树,巧了,小弟我之前有过一篇文章对此进行介绍,可移步《AST 抽象语法树》

比如一句简单的变量声明:

const msg = 'Hello Juejin'

生成的抽象语法树大概如下图所示:

image.png

注意:这里面还有个概念叫做预解析(PreParser),比如有些函数在初次运行的时候不需要立即被执行,那么直接完整的解析这部分代码成 AST 就有些浪费性能,V8 引擎会使用延迟解析(Lazy Parsing)的方案,只解析暂时需要的内容,对函数的全量解析则等函数被调用时才会执行。而如果函数始终没有没调用,那么是不会被转换成 AST 的。

2. Ignition

通过 V8 引擎内的 Ignition 模块将生成的 AST 转换为字节码(Byte-code)。字节码是一种包含执行程序,由一序列 op 代码/数据对组成的二进制文件,是一种中间码。之所以要转成字节码而不是直接转换成机器码,是因为平台(windows/Linux 等)不同,它们各自的 cpu 可以识别的机器码也不相同。从 AST 直接转换成各种 cpu 都可运行的机器码就不如先转成 V8 引擎约定好的可以跨平台运行的字节码,等到真正运行时再根据平台转成不同的 cpu 指令,这样效率更高。
另外,Ignition 模块会收集 TurboFan 优化所需要的信息,比如函数参数的类型信息,有了类型才能进行真实的运算。
注:字节码是先转成汇编语言再转成机器语言的。

3. TurboFan

V8 引擎在编译 js 时,还会用到 TurboFan 模块,直接将字节码优化成机器码,并在需要时进行反优化(Deoptimization),变回字节码。比如 Ignition 模块收集到有一个函数方法会被多次执行这个信息,该函数就会被 Ignition 标记为热函数。由 TurboFan 将这个函数转为机器码保存起来,下次再次执行就可以直接运行了。 但是,如果函数的参数类型在下次执行时被转换了,比如 a + b,开始时 ab 都是数字,转换成的机器码执行的是数字相加的操作,后面 ab 变成字符串了,那么 + 执行的就是字符串拼接的操作,原有的机器码就不能用了,需要反优化回字节码继续执行。由此可知,在执行函数的时候,最好不要让参数的类型前后不一。(就这点而言,ts 编译的代码执行速度会更快点)

图示

上述内容可以简单总结个流程图,如下:

image.png

感谢.gif 点赞.png