一 简介
本文是一个 V8 编译原理知识的介绍文章,旨在让大家感性的了解 JavaScript 在 V8 中的解析过程。本文主要的撰写流程如下:
- 解释器和编译器:计算机编译原理的基础知识介绍
- V8 的编译原理:基于计算机编译原理的知识,了解 V8 对于 JavaScript 的解析流程
- V8 的运行时表现:结合 V8 的编译原理,实践 V8 在解析流程中的具体运行表现
本文仅代表个人观点,文中若有错误欢迎指正。
二 解释器和编译器
大家可能一直疑惑的问题:JavaScript 是一门解释型语言吗?要了解这个问题,首先需要初步了解什么是解释器和编译器以及它们的特点是什么。
1 解释器
解释器的作用是将某种语言编写的源程序作为输入,将该源程序执行的结果作为输出,例如 Perl、Scheme、APL 等都是使用解释器进行转换执行:
2 编译器
编译器的设计是一个非常庞大和复杂的软件系统设计,在真正设计的时候需要解决两个相对重要的问题:
- 如何分析不同高级程序语言设计的源程序
- 如何将源程序的功能等价映射到不同指令系统的目标机器
中间表示(IR)
中间表示(Intermediate Representation,IR)是程序结构的一种表现方式,它会比抽象语法树(Abstract Syntax Tree,AST)更加接近汇编语言或者指令集,同时也会保留源程序中的一些高级信息,具体作用包括:
- 易于编译器的错误调试,容易识别是 IR 之前的前端还是之后的后端出的问题
- 可以使得编译器的职责更加分离,源程序的编译更多关注如何转换成 IR,而不是去适配不同的指令集
- IR 更加接近指令集,从而相对于源码可以更加节省内存空间
优化编译器
IR 本身可以做到多趟迭代从而优化源程序,在每一趟迭代的过程中可以研究代码并记录优化的细节,方便后续的迭代查找并利用这些优化信息,最终可以高效输出更优的目标程序:
优化器可以对 IR 进行一趟或者多趟处理,从而生成更快执行速度或者更小体积的目标程序(例如找到循环中不变的计算并对其进行优化从而减少运算次数),也可能用于产生更少异常或者更低功耗的目标程序。除此之外,前端和后端内部还可以细分为多个处理步骤,具体如下图所示:
3 两者的特性比较
解释器和编译器的具体特性比较如下所示:
需要注意早期的 Web 前端要求页面的启动速度快,因此采用解释执行的方式,但是页面在运行的过程中性能相对较低。为了解决这个问题,需要在运行时对 JavaScript 代码进行优化,因此在 JavaScript 的解析引擎中引入了 JIT 技术。
4 JIT 编译技术
JIT (Just In Time)编译器是一种动态编译技术,相对于传统编译器而言,最大的区别在于编译时和运行时不分离,是一种在运行的过程中对代码进行动态编译的技术。
5 混合动态编译技术
为了解决 JavaScript 在运行时性能较慢的问题,可以通过引入 JIT 技术,并采用混合动态编译的方式来提升 JavaScript 的运行性能,具体思路如下所示:
采用上述编译框架后,可以使得 JavaScript 语言:
- 启动速度快:在 JavaScript 启动的时候采用解释执行的方式运行,利用了解释器启动速度快的特性
- 运行性能高:在 JavaScript 运行的过程中可以对代码进行监控&#