V8引擎详解(三)——从字节码看V8的演变

前言

本文是V8引擎详解系列的第三篇,重点内容是关于了解字节码的概念,以及字节码在V8引擎演变过程当中的重要影响,同时帮您梳理v8引擎的架构帮助您更好的了解V8引擎架构,文末会有已经完成的系列文章的连接,本系列文章还在不断更新欢迎持续关注。前端

字节码概念

1、什么是字节码

wiki百科中字节码的描述是这样的:java

字节码(英语:Bytecode)一般指的是已经通过编译,但与特定机器代码无关,须要解释器转译后才能成为机器代码的中间代码。字节码一般不像源码同样可让人阅读,而是编码后的数值常量、引用、指令等构成的序列。浏览器

按照做者的对字节码的理解大概是这样:
计算机只能识别二进制代码,而二进制代码(指令集)是不合适人类书写和阅读的,不一样的CPU架构对应的指令集也是彻底不一样的,为了克服这个问题,大神们就创造出了适合人类的语言,也就是所谓的 “高级” 语言,这些高级语言与人类的天然语言以及数学公式的使用是很是接近的,并且不用考虑CPU架构差别。而高级语言和二进制代码之间的差别是至关大的,直接转换会很是麻烦,这时就有了两者中间的代码——字节码缓存

2、字节码的优势

要了解字节码的优势,最直观的方式就是直接看字节码给java带来了什么,早期java推广的口号就是Compile Once,Run anywhere(一次编译处处运行),Java源代码通过编译程序编译以后生成扩展名为.class的字节码文件。再经过JVM将字节码翻译为机器的计算机指令(目标机器必需要安装对应的JVM(java虚拟机))。
Java 语言使用字节码的方式,必定程度的解决了解释性语言执行效率低的问题,同时因为字节码不针对一种特定的机器,因此Java程序无须从新编译就可在多种不一样的计算机上运行。架构

字节码的优势总结来讲就是:函数

  • 不针对特定CPU架构
  • 比原始的高级语言转换成机器语言更快

V8的演变

1、V8早期架构

V8未诞生以前,早期最主流的JavaScript引擎是JavaScriptCore引擎。javasSriptCore是经过生成字节码再将字节码转化成二进制代码的方式运行的,而V8诞生的使命就是性能的极致,Google以为这这种架构生成字节码会浪费时间,V8早期采用了直接生成机器码的方式以下图:post

(图片来源: blog.itpub.net/69912579/vi…

咱们一块儿来看一下V8早期架构如何执行js代码的:性能

  • 第一步,将js源代码转化成AST(抽象语法树)
  • 第二步,经过Full-Codegen引擎编译AST变成二进制文件,而后直接执行这些二进制文件。
  • 第三步,在执行二进制代码的过程当中,标记重复执行的函数,将标记的代码经过Crankshaft引擎进行优化编译生成效率更高的二进制代码,再次运行到这个函数时使用效率更高的二进制代码。

同时采用了将二进制代码缓存(缓存到内存和硬盘上)的策略来省去重复编译的时间,在初期这种架构的确带来了速度上的改善。优化

2、为何要引入字节码

随着网页的复杂化以及移动端的流行,早期的架构也带来很是多的问题,编码

1.内存占用问题

最核心的问题就是空间占用问题,在V8执行的过程会将js源代码转化成二进制代码而且将二进制代码存储到内存中,退出进程后会将二进制代码存储到硬盘上。
将js源码转化成的二进制代码占用的内存空间是很是巨大的,若是说一个js源码的文件大小是1M,那么生成的二进制代码可能就是十几M,而早期手机的内存广泛不高,过分占用会致使性能大大下降。

2.代码复杂度过高

上文提到过不一样的CPU架构对应的指令集是彻底不一样的,而市面上CPU架构的种类又很是多,那么将AST转化为二进制代码的Full-Codegen引擎以及优化编译的Crankshaft引擎要针对不一样的CPU架构编写代码,这个复杂程度及工做量可想而知,而对字节码进行编译能够大大的减小这个工做量,大概以下图:

3.一个Bug

咱们先来看一下这个 bug ,大概描述是这样的:
Bug的报告人在当时的Chrome浏览器下重复加载Facebook,并打开了各项监控发现:第一次加载时 v8.CompileScript 花费了 165 ms,而重复加载时发现真正耗时高的js代码并无被缓存,致使重复加载时编译的时间和第一次加载的消耗大体相同。

致使这个问题的缘由其实也很好理解,以前提到过由于二进制代码占用内存空间大,根据惰性编译的优化原则,因此V8并不会将全部代码进行编译只会编译最外层的代码,而在函数内部的代码会在第一次调用时编译,好比:

若是浏览器只缓存最外层代码,那么对咱们前端高度工程化的模块来讲会致使里面的关键代码却没法被缓存,这也是致使上述bug的主要缘由。

3、V8现有架构

为了解决上述的问题,V8开始采用引入字节码的架构,最终采用了以下图的架构:

(图片来源: blog.itpub.net/69912579/vi…
咱们来一块儿看一下V8现有架构是如何执行js代码的:

  • 第一步,将js源代码转化成AST(抽象语法树)
  • 第二步,经过Ignition解释器将AST编译成字节码,开始逐句对字节码进行解释成二进制代码并执行。
  • 第三步,在解释执行的过程当中,标记重复执行的热点代码,将标记的代码经过Turbofan引擎进行编译生成效率更高二进制代码,再次运行到这个函数时便只执行高效代码而再也不解释执行字节码。

V8引入了字节码的架构模式后明显的解决了以下问题:

  • 启动时间较长:启动时只须要编译出字节码,而后逐句执行字节码,编译出字节码的速度可远远快于编译出二进制代码的速度。
  • 内存占用较大:字节码的空间占用也是远远低于二进制代码的空间占用。
  • 代码复杂度过高:大大下降了V8适应不一样CPU所须要的代码复杂程度。

最后咱们再来看一下新架构和原有架构比较带来的效果:

总结

本文主要经过 字节码 的特色来了解了V8引擎的架构演变过程分析了演变的缘由,以及对架构进行了一次梳理来帮助咱们后面继续了解V8,若是有什么错误,请在评论中和做者一块儿讨论,若是您以为本文对您有帮助请帮忙点个赞,感激涕零。

参考文章

blog.itpub.net/69912579/vi…
time.geekbang.org/column/arti…

相关文章

V8引擎详解(一)——概述
V8引擎详解(二)——AST
V8引擎详解(三)——从字节码看V8的演变

相关文章
相关标签/搜索