- Script:表示JavaScript代码,即包含源代码,又包含编译之后生成的本地代码,即是编译入口,又是运行入口;
- Execution:运行代码的辅组类,包含一些重要函数,如Call函数,它辅组进入和执行Script代码;
- JSFunction:需要执行的JavaScript函数表示类;
- Runtime:运行这些本地代码的辅组类,主要提供运行时所需的辅组函数,如:属性访问、类型转换、编译、算术、位操作、比较、正则表达式等;
- Heap:运行本地代码需要使用的内存堆类;
- MarkCompactCollector:垃圾回收机制的主要实现类,用来标记、清除和整理等基本的垃圾回收过程;
- SweeperThread:负责垃圾回收的线程 。
![认识 V8 引擎](http://img.jiangsulong.com/220416/114303N26-8.jpg)
文章插图
先根据需要编译和生成这些本地代码,也就是使用编译阶段那些类和操作 。在V8中,函数是一个基本单位,当某个JavaScript函数被调用时,V8会查找该函数是否已经生成本地代码,如果已经生成,则直接调用该函数 。否则,V8引擎会生成属于该函数的本地代码 。这就节约了时间,减少了处理那些使用不到的代码的时间 。其次,执行编译后的代码为JavaScript构建JS对象,这需要Runtime类来辅组创建对象,并需要从Heap类分配内存 。再次,借助Runtime类中的辅组函数来完成一些功能,如属性访问等 。最后,将不用的空间进行标记清除和垃圾回收 。
2.3.优化回滚因为V8是基于AST直接生成本地代码,没有经过中间表示层的优化,所以本地代码尚未经过很好的优化 。于是,在2010年,V8引入了新的编译器-Crankshaft,它主要针对热点函数进行优化,基于JavaScript源代码开始分析而非本地代码,同时构建Hydroger图并基于此来进行优化分析 。
Crankshaft编译器为了性能考虑,通常会做出比较乐观和大胆的预测—代码稳定且变量类型不变,所以可以生成高效的本地代码 。但是,鉴于JavaScript的一个弱类型的语言,变量类型也可能在执行的过程中进行改变,鉴于这种情况,V8会将该编译器做的想当然的优化进行回滚,称为优化回滚 。
示例如下:
var counter = 0;function test(x, y) {counter++;if (counter < 1000000) {// do somethingreturn 'jeri';}var unknown = new Date();console.log(unknown);}
该函数被调用多次之后,V8引擎可能会触发Crankshaft编译器对其进行优化,而优化代码认为示例代码的类型信息都已经被确定 。但,由于尚未真正执行到new Date()这个地方,并未获取unknown这个变量的类型,V8只得将该部分代码进行回滚 。优化回滚是一个很耗时的操作,在写代码过程中,尽量不要触发优化该操作 。在最近发布的 V8 5.9 版本中,新增了一个 Ignition 字节码解释器,TurboFan 和 Ignition 结合起来共同完成JavaScript的编译 。这个版本中消除 Cranshaft 这个旧的编译器,并让新的 Turbofan 直接从字节码来优化代码,并当需要进行反优化的时候直接反优化到字节码,而不需要再考虑 JS 源代码 。
2.4.隐藏类与内嵌缓存2.4.1.隐藏类在执行C++代码时,仅凭几个指令即可根据偏移信息获取变量信息,而JavaScript里需要通过字符串匹配来查找属性值的,这就需要更多的操作才能访问到变量信息,而代码量变量存取是十分频繁的,这也就制约了JavaScript的性能 。V8借用了类和偏移位置的思想,将本来通过属性名匹配来访问属性值的方法进行了改进,使用类似C++编译器的偏移位置机制来实现,这就是隐藏类 。
隐藏类将对象划分成不同的组,对于组内对象拥有相同的属性名和属性值的情况,将这些组的属性名和对应的偏移位置保存在一个隐藏类中,组内所有对象共享该信息 。同时,也可以识别属性不同的对象 。示例如下:
![认识 V8 引擎](http://img.jiangsulong.com/220416/114303FL-9.jpg)
文章插图
使用Point构造了两个对象p和q,这两个对象具有相同的属性名,V8将它们归为同一个组,也就是隐藏类,这些属性在隐藏类中有相同的偏移值,p和q共享这一信息,进行属性访问时,只需根据隐藏类的偏移值即可 。由于JavaScript是动态类型语言,在执行时可以更改变量的类型,如果上述代码执行之后,执行q.z=2,那么p和q将不再被认为是一个组,q将是一个新的隐藏类 。
2.4.2.内嵌缓存正常访问对象属性的过程是:首先获取隐藏类的地址,然后根据属性名查找偏移值,然后计算该属性的地址 。虽然相比以往在整个执行环境中查找减小了很大的工作量,但依然比较耗时 。能不能将之前查询的结果缓存起来,供再次访问呢?当然是可行的,这就是内嵌缓存 。
推荐阅读
- 认识显卡:从入门到理赔
- 大家都在用搜索引擎,你知道搜索引擎是如何找到你要的内容的吗?
- 眼影|零基础学化妆,认识化妆产品-眼影
- 4种方法实时监控Linux日志文件
- Tomcat优化大全,进来看了,真就会了
- MySQL8.0真香
- 没有人比我更懂电流,今天带你重新认识电流
- 最容易被贴罚单的9大交通标志,你认识吗?
- 分布式架构的总结
- 茶马古道的认识,真正的茶马古道是做什么的