解释器:
像是短跑选手, 启动很快, 但是长时间的跑步就有点慢了.
编译器:
像是长跑选手, 启动慢一点, 但是长跑就比较厉害.
v8 = 编译器 + 解释器
v8使用JIT(Just In Time)技术, 就是编译器+解释器.
意思就是开始先用解释器执行代码,
但是碰到重复执行的热点代码, 就优化到解释器中执行.
这样第一次启动也快了, 长时间的执行也能逐步优化.
v8的执行流程
准备阶段:
在 V8 启动执行 JavaScript 之前,还需要准备执行 JavaScript 时所需要的一些基础环境,
- “堆空间” — 内存模型
- “栈空间” — 内存模型
- “全局执行上下文” — 执行过程中的全局信息
- “全局作用域” — 用来查找变量
- “消息循环系统” — 消息驱动器, 消息队列, 让v8运行起来
- “内置函数” — 内置的方法
执行阶段:
基础环境准备好之后,就可以执行的 JavaScript 代码了
- 首先 v8 需要将代码结构化成抽象语法树(AST), 这样的树形结构 v8 比较方便遍历和解析.
- 生成AST的同时, 还会生成 作用域, 作用域是用来查找变量的
- 生成字节码, AST 和 机器代码的中间代码,
- 进入解释器, 解释字节码, 并且输出执行结果
- 如果某段代码重复执行, 则这段代码会被标记为 热点代码,
- 标记完了v8将这段代码进入优化编译器, 将热点代码优化编译后得到二进制代码
- 再次执行热点代码的时候, 则直接使用二进制代码
- 一旦执行过程中对象结构被修改了, 会触发反优化操作,下次执行就会回退到解释器中执行了.
总结
运行一段代码, 就像是做一道菜,
- 需要将食材处理好, 切块, 切片, 切丁(AST),
- 然后用小碗装起来(作用域), 什么黄瓜条, 萝卜丁, 肉块, 方便找到使用,
- 再然后就开始炒菜了, 按着菜谱(字节码), 先放油,再放辣椒, 再放肉, 就是一步一步的放入锅里(解释器)
- 然后就装盘了, 开吃.
- 然后发现真好吃, 还想再吃一盘, 但是老得切 葱姜蒜 (热点代码)好麻烦, 就每次多切一点葱姜蒜, 装起来, 下次再炒的时候, 直接用, 减少做饭的时间.
- 但是呢, 有时候我们可能需要的蒜, 需要整颗的, 就需要重新拨一个蒜(反优化操作).
这样其实就是v8执行一段代码的过程.