V8引擎工作机制
本文也发布于本人的知乎专栏:https://zhuanlan.zhihu.com/p/394578040
0.前言
在翻译文章从嵌入V8开始中,从一个较为黑盒的角度介绍了如何将V8引擎嵌入到自己的C++项目中,并简单的介绍了一些相关API和概念。
本文将会从一个概念层次上介绍V8引擎的工作机制。涉及的源码不多,仅对一些接口进行介绍,后续会在其他文章中进行详细的源码分析。
1. 工作机制演化
V8引擎主要能力是将JavaScript源码解释编译,并运行。现阶段的工作机制可以分为以下几个阶段:
- Parser:解析器,将JS源码解析为AST(抽象语法树,Abstract Syntax Tree);
- Ignition:基于寄存器的解释器,用于将AST转换为字节码;
- TurboFan:基于Ignition生成的字节码,生成对应平台的机器码,并对其进行优化和反优化。
总体而言可以用下图来形象地表示:
其实,该工作流程其实是经过较大的重构的。在5.9版本之前,V8引擎是没有生成字节码这一过程的,而是直接将AST通过Full-codegen快速生成为未优化的机器码,之后再通过Crankshaft对热点函数进行优化编译。
这种方式跳过了字节码,极大减少了转换时间。
然而这种优化方式还是过于激进了,谷歌又对其进行了大幅度重构,最终产生了现在的引擎工作机制。这次重构的起因是Chrome的一次bug上报:crbug.com/593477,对Facebook第一次加载时,v8.CompileScript
花费了165ms,再次加载加入v8.ParseLazy
,花费时间增长到376ms。然而期望的情况时缓存功能应该对JS脚本的解析结果进行了缓存,花费时间不应该这么长。
后面经过分析发现,因为机器码占用空间过大,无法一次性将所有JS代码编译成机器码缓存下来,所以只编译最外层的JS代码,内部代码则留到第一次被调用时再编译。然而Facebook的开发者将各个独立module编译成单独的文件,其中用到很多闭包,