我是靠谱客的博主 鲤鱼万宝路,最近开发中收集的这篇文章主要介绍如何将二进制机器码转换成汇编指令_v8是如何执行一段JavaScript代码的?,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1、什么是v8? V8是JavaScript虚拟机的一种,可以理解为一个翻译程序,将人类能够理解的编程语言翻译成机器能够理解的机器语言。(用来执行js代码) a640fd787003a19b7e71c95bff0966da.png 市面上有多种JavaScript引擎:spiderMonkey、V8、JavaScriptCore 在v8之前,所有JavaScript虚拟机都是采用解释执行的方式。V8引入了即时编译,混合编译执行和解释执行两种手段,给JavaScript的执行速度带来极大的提升。 V8还引入了惰性编译、内联缓存、隐藏类等机制。进一步优化的JavaScript的编译执行速度。 22eda966f7e5f8595812422176b2fabb.png 2、高级语言为什么先编译后执行(CPU怎么执行机器代码的)? 可以把CPU看成是非常小的运算机器。我们可以通过二进制指令和CPU进行通信。比如给CPU发出 1000100111011000 的二进制指令,这条指令的意思是将寄存器中的数据移到另一个寄存器。 CPU 只能识别二进制指令 所以需要一个汇编编译器,将汇编代码转为机器代码。 002979d56893edf9e4195e90581f7615.png 但是汇编语言依然是复杂繁琐的。 首先,不同的CPU有着不同的指令集。所以你需要为每种架构的CPU编写特定的汇编代码。 c5021801da2aca74cedfd375e53e033c.png 在编写汇编代码时,我们需要了解和处理器架构相关的硬件知识。比如需要寄存器、内存、操作CPU等。 因此需要一种屏蔽计算机架构细节的语言。能适应多种CPU的架构语言。比如C、C++、JavaScript、Java、C#、python 和汇编语言一样,处理器不能识别高级语言。通过两种方式来执行这些代码。 第一种是解释执行。 需要将输入的源代码通过解析器编译成中间代码,之后直接使用解释器解释执行中间代码,然后直接输出结果。 9c9573aa01628b5edc7223b37e0be08e.png 第二种是编译执行。 也需要将源代码转为中间代码,然后编译器再将中间代码编译成机器代码。通常编译成的机器代码是二进制形式存在的。需要执行这段程序直接执行二进制文件就可以了。 还可以使用虚拟机将编译后的机器代码保存在内存中,直接执行内存中的二进制代码。 8420f3d039ae17e66a9d2b9f6ae1df8b.png 3、v8是如何执行一段JavaScript代码的? 其主要核心流程是编译和执行两步。首先将JS代码转换为低级中间代码或者机器能够理解的代码,然后再执行并输出结果。 可以把V8看做是虚拟机,模拟实际计算机的各种功能来实现代码的执行。 比如:模拟实际计算机的CPU、堆栈、寄存器等。 V8 采用混合编译执行和解释执行两种手段。称为JIT(just in time) 因为这两种各有各自的优缺点。解释执行的启动速度快,但执行速度慢。而编译执行的启动速度慢,执行速度快。 22eda966f7e5f8595812422176b2fabb.png 首先,在v8启动执行js之前,需要准备执行时的准备环境,包括堆空间、栈空间、执行上下文、消息循环系统、内置函数等。 这 些内容都需要在执行JavaScript过程中需要的。 比如: JavaScript全局执行上下文就包含了执行过程中的执全局信息,比如内置函数、全局变量等。 全局作用域就包含了一些全局变量,在执行过程中数据都需要保存在内存中。 V8 采用了堆和栈的内存管理方式,所以v8还需要初始化内存中的堆和栈结构。 另外还需要初始化消息循环系统,它如同v8的心脏,不断接受消息,并处理。 基础环境准备好后,接下来就可以向v8提交需要执行的代码了。 结构化源代码,生成抽象语法树(AST),即便于v8理解的结构。同时生成相关的作用域,作用域中存放变量。 接下来生成字节码。是介于机器码和AST的中间代码。 解释器可以执行解释执行字节码。 在解释执行的过程中,如果 发现某一段代码被重复多次执行,那么就会这段代码标记为热点代码。 V8会将这段字节码丢给优化编译器,优化编译器会把字节码编译为二进制代码,然后再对二进制代码进行优化,优化后的二进制代码执行效率会大幅提升。 但是和静态语言不同,JavaScript是动态语言,对象的结构和属性是可以在运行期间任意修改的,而优化后的代码只针对某种固定结构,一旦在执行过程中,对象的结构被动态修改了,那么优化后的代码势必变成无效的代码。 这时候就要优化编译器执行反优化操作,下次执行就退回解释器解释执行。 总结: 1 、初始化基础环境; 2、解析源码生成 AST 和作用域; 3、依据 AST 和作用域生成字节码; 4、解释执行字节码;监听热点代码; 5、优化热点代码为二进制的机器代码; 6、反优化生成的二进制机器代码。 4、v8为什么又要引入字节码? 机器代码缓存 0908ef2a77009fcf52872484cf2f7365.png 从图中可以看出,编译所消耗的时间和执行所消耗的时间是差不多的,试想一下,如果在浏览器中再次打开相同的页面,当页面中的 JavaScript 文件没有被修改,那么再次编译之后的二进制代码也会保持不变, 这意味着编译这一步白白浪费了 CPU 资源,因为之前已经编译过一次了。 这就是 Chrome 浏览器引入二进制代码缓存的原因,通过把二进制代码保存在内存中来消除冗余的编译,重用它们完成后续的调用,这样就省去了再次编译的时间。 V8 使用两种代码缓存策略来缓存生成的代码。
  • 首先,是 V8 第一次执行一段代码时,会编译源 JavaScript 代码,并将编译后的二进制代码缓存在内存中,我们把这种方式称为内存缓存(in-memory cache)。
  • 将代码缓存到硬盘上,这样即便关闭了浏览器,下次重新打开浏览器再次执行相同代码时,也可以直接重复使用编译好的二进制代码。
5d5b4c929e40432c7e1f4f413a70deaf.png 字节码降低内存占用 V8 在执行 JavaScript 代码的过程中,会将 JavaScript 代码转换为未经优化的二进制代码,你可以对照下图中的 JavaScript 代码和二进制代码的: b68858699dbd7f2157d51819997041aa.png 二进制代码所占用的内存空间是 JavaScript 代码的几千倍, V8 团队为了提升 V8 的启动速度,采用了 惰性编译 ,其实惰性编译除了能提升 JavaScript 启动速度,还可以解决部分内存占用的问题。 c108df065b45f8f3e4f8044c02de9a93.png 根据惰性编译的原则,当 V8 首次执行上面这段代码的过程中,开始只是编译最外层的代码,那些函数内部的代码,如下图中的黄色的部分,会推迟到第一次调用时再编译。 为了解决缓存的二进制机器代码占用过多内存的问题,早期的 Chrome 并没有缓存函数内部的二进制代码,只是缓存了顶层次的二进制代码,比如上图中红色的区域。 如果浏览器只缓存顶层代码,那么闭包模块中的代码将无法被缓存,而对于高度工程化的模块来说,这种模块式的处理方式到处都是,这就导致了一些关键代码没有办法被缓存。 所以采取只缓存顶层代码的方式是不完美的,V8 团队对早期的 V8 架构进行了非常大的重构,具体地讲,抛弃之前的基线编译器和优化编译器,引入了字节码、解释器和新的优化编译器。 131f349e8a5bcff9eded2cd92ce0c6e6.png 从图中可以看出,字节码虽然占用的空间比原始的 JavaScript 多,但是相较于机器代码,字节码还是小了太多。 有了字节码,无论是解释器的解释执行,还是优化编译器的编译执行,都可以直接针对字节来进行操作。由于字节码占用的空间远小于二进制代码,所以浏览器就可以实现缓存所有的字节码,而不是仅仅缓存顶层的字节码。 虽然采用字节码在执行速度上稍慢于机器代码,但是整体上权衡利弊,采用字节码也许是最优解。 字节码如何提升代码启动速度? 87632b6461a2ebacb1ba2193b9e066d2.png 从图中可以看出,生成机器代码比生成字节码需要花费更久的时间,但是直接执行机器代码却比解释执行字节码要更高效,所以在快速启动 JavaScript 代码与花费更多时间获得最优运行性能的代码之间,我们需要找到一个平衡点。 解释器可以快速生成字节码,但字节码通常效率不高。 字节码如何降低代码的复杂度? 8ec5bde10e14a35a6ca1d6cb137e8042.png 这意味着基线编译器和优化编译器要针对不同的体系的 CPU 编写不同的代码,这会大大增加代码量。 引入了字节码,就可以统一将字节码转换为不同平台的二进制代码, 你可以对比下执行流程: ae5e2705573a65126d2db502c815ba04.png 因为字节码的执行过程和 CPU 执行二进制代码的过程类似,相似的执行流程,那么将字节码转换为不同架构的二进制代码的工作量也会大大降低,这就降低了转换底层代码的工作量。 总结: 不过随着移动设备的普及,V8 团队逐渐发现将 JavaScript 源码直接编译成二进制代码存在两个致命的问题:
  • 时间问题:编译时间过久,影响代码启动速度;
  • 空间问题:缓存编译后的二进制代码占用更多的内存。
这两个问题无疑会阻碍 V8 在移动设备上的普及,于是 V8 团队大规模重构代码,引入了中间的字节码。字节码的优势有如下三点:
  • 解决启动问题:生成字节码的时间很短;
  • 解决空间问题:字节码占用内存不多,缓存字节码会大大降低内存的使用;
  • 代码架构清晰:采用字节码,可以简化程序的复杂度,使得 V8 移植到不同的 CPU 架构平台更加容易。

最后

以上就是鲤鱼万宝路为你收集整理的如何将二进制机器码转换成汇编指令_v8是如何执行一段JavaScript代码的?的全部内容,希望文章能够帮你解决如何将二进制机器码转换成汇编指令_v8是如何执行一段JavaScript代码的?所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部