什么是V8
一个接受Javascript代码,编译代码然后执行的C++程序,编译后的代码可以在多种操作系统多种处理器上运行
主要工作
- 编译js代码
- 处理调用栈
- 内存分配
- 垃圾的回收
重要组件
大部分js引擎在编译和执行js代码,都会用到三个重要的组件
- 解析器:负责将JS源代码解析成抽象语法树(AST)
- 解释器: 负责将AST解释成字节码bytecode,同时解释器也有直接解释执行bytecode的能力
- 编译器:负责编译出运行更加高效的机器代码
V8 5.8以前
在v8 5.8之前没有解释器,有两个编译器。
JS有解析器解析后生成抽象语法树AST,然后有Full-codegen编译器直接使用AST来编译出机器代码,而不进行中间转换。
Full-codegen编译起也被称为基准编译器,因为它生成的是一个基准未被优化的机器代码。这样做的好处是,当我们第一次执行js的时候,就是直接使用的高效的机器代码。因为没有中间的字节码产生,就不需要解释器。
当代码运行一段时间后,V8引擎中的分析线程,收集了足够的数据,来帮助另一个编译器Crankshaft
来做代码优化。
然后需要优化的源码重新解析生成AST,然后CrankShaft
根据生成好的ASt再生成优化后的机器代码,来提升运行的一个效率。所以CankShaft
又成为优化编译器。
缺点
- 机器码占用大量内存
- 缺少中间层机器吗,无法实现一些优化策略
- 无法很好的支持和优化JS的新语法特性
优化后的V8
语法树的解析还是基本保持一致的,但在获得抽象语法树之后,v8引擎加入了解释器Ignition,语法树通过解释器Ignition生成了bytecode字节码,此时AST就被清除掉了,释放内存空间,生成bytecode直接被解释器执行,同时生成的bytecode将作为基准执行模型,字节码更加简洁。生成的bytecode大小相当于等效的基准机器代码的25到50%左右。
在代码不断运行过程中,解释器收集到了很多可以用来优化代码的信息,比如变量的类型、那些函数执行的频率较高,这些信息被发送给编译器TruboFan,编译起TruboFan会根据这些信息来编译出经过优化的机器代码。
优化的机器码也有可能被反向编译为字节码,这个过程叫deoptimization
优化策略
- 函数只声明未被调用,不会被解析成AST
- 函数只被调用一次,bytecode直接被解释执行
- 如果函数被调用多次,可能会被标记为热点函数,可能会被编译成机器代码
优点
- 由于不需要直接编译成机器码,而是使用了中间层的字节码,字节码生成速度远远大于机器吗,所以网页初始化解析执行js的时间缩短了
- 在生成的优化机器代码时,不需要再从源码开始编译,而是直接使用字节码编译。而且需要deoptimization时,只需要回归到中间层字节码解释执行就可以了