一、V8 引擎的介绍
一、V8 引擎的介绍
Google V8 引擎的作用
用来解析 javascript 代码,它内置jit (实时编译),用来编译和执行JS代码,处理调用栈,内存分配,处理垃圾回收
V8和其他高级语言具有相同的能力,具备解析器,解释器,编译器功能
首先V8先讲代码解析成 AST 语法树
在将AST语法树,解释成字节码bytecode,注意(解释器也具有直接解释执行bytecode的能力)
在将bytecode编译成机器代码
早期—V8的编译流程
据说在早起的V8引擎中,没有解释器,却有两个编译器,它的编译流程是这样的:
JS由解析器,转换为 AST语法树,然后由Full-codegen编译器,直接使用AST,来编译成机器代码,而不进行中间转换,Full-codegen编译器也被称为基准编译器,因为它生成的是一个基准的,未被优化的机器代码,这样做的好处是,当你第一次执行js的时候,就是直接使用了高效的机器代码,因为没有中间的字节码产生,所以就不需要解释器,当代码运行一段时间后,v8引擎中的分析线程收集了足够的数据,来帮助另一个编译器Crankshaft来做代码优化,然后需要优化的源码,重新解析生成AST,然后Crankshaft使用生成好的AST,在生成优化后的机器代码,来提升运行的一个效率,所以Crankshaft的编译器又被称为优化编译器。
但是这个架构也带来了一定的问题
1、机器码占用大量内存
2、缺少中间层机器码,无法实现一些优化策略
3、无法很好的支持和优化JS的新语法特效
新的V8编译流程:
JS由解析器,解析成AST
AST 由 lgniton 基准解释器,生成bytecode 字节码,此时AST被清除,释放内存空间,bytecode由解释器执行,同时生成的bytecode将作为基准执行模型,字节码更加简洁,生成的bytecode大小相当于等效的基准机器代码25%-50%左右,在代码的不断的运行过程中,解释器收集到了很多,可以用来优化代码的信息,比如变量的类型,那些函数执行的频率较高,这些信息被发送给编译器,V8引擎新的编译器TurboFan,会根据这些信息和字节码,来编译出经过优化的机器代码。
这里简单说几个v8引擎在处理JS过程中的一些优化策略,
1、如果函数只是声明,却没有被调用,则改函数不会被解析生成AST,也就不会生成字节码
2、函数只被调用一次,bytecode直接被解释执行
3、函数被调用多次,可能会被标记为“热点函数”,可能会被编译成机器代码
在某些情况下,机器代码还可能被逆向还原成字节码,这个过程叫做 deoptimization,这是因为Javascript是一个动态语言,会导致lgnition收集到的信息是错误的
比如有一个 sum函数
function sum(x, y) {
return x + y
}
在函数声明时,JS引擎不知道x,y是什么类型的,但是多次调用后,传入的都是int类型,sum函数被识别为热点函数,此时编译成机器码就假定了x和y都是int类型,如果你调用x,y传入string类型,机器码不知道如何处理,就需要执行 deoptimization 操作了
注意 deoptimization 操作会带来性能的影响,如果你看到有人这样用了,你就可以去装B了。
新的V8还有什么优点
因为不需要一开始就解析成机器码,所以bytecode体积比机器码小,网页初始化解析执行JS的时间缩短了
进行 deoptimization 操作等情况的时候,依赖bytecode了,会比依赖原有的AST更快
v8 主要组成
Parser: 解析器,负责将源代码解析成AST
Ignition: 解释器,负责将AST转换成字节码并执行,同时会标记热点代码
TurboFan: 编译器,负责将热点代码编译成机器码并执行
Orinoco: 垃圾回收器,负责进行内存空间回收