V8 引擎是什么?
V8引擎是谷歌开发的开源JavaScript引擎与WebAssembly引擎,它是谷歌浏览器和Node.js的核心部分,主要负责将我们编写的js代码转化为机器能够执行的高效机器码
V8引擎的核心是:
- 核心点1:它不是浏览器,是引擎,是翻译官
- 核心点2:它不只用于浏览器,也用于node.js,所以它是跨端的
- 核心点3:它是高性能的,执行效率非常高
V8 如何实现高性能?
V8实现高性能的核心在于它独特的工作流程,尤其是"JIT编译"技术.它不像传统解释器一行行解释执行,也不像编译器完全编译完再执行,而是结合了两者的优点。
1、解析与AST
- V8的parser会将js源码解析成AST
- Babel、Eslint等工具的工作原理也是操作AST
2、解释执行与JIT编译
- Ignition是V8的解释器,它会将AST转换为字节码并执行
- 与此同时,V8会收集代码的执行信息,如果多个函数被多次调用(成功“热点函数”),那么它就会被标记出来,送到TurboFan这个优化编译器中
3、优化编译与内联缓存
- TurboFan会将热点函数编译成高效的“机器码”,下次再执行这个函数时,就直接运行机器码,速度极快
- 内联缓存是加速属性访问的关键机制,V8会猜测对象的形状(也就是“隐藏类”),如果下次访问的时候对象结构不变,那么就直接使用缓存的机器码,否则就重新编译,这样就避免了复杂的查找过程
- 隐藏类为內联缓存提供了基础。內联缓存通过隐藏类来识别对象的结构,从而缓存属性访问的位置。
- 隐藏类和內联缓存是V8优化属性访问的两种机制,它们不是同一个东西,但是紧密合作。隐藏类描述了对象的结构,內联缓存则利用隐藏类来缓存属性访问的位置。在编写代码时,我们应该尽量保持对象结构的稳定,以提高內联缓存的命中率,从而提升性能。
这也解释了为什么js代码中 保持对象结构的稳定 有利于性能提升。例如避免在构造函数后动态添加属性
4、垃圾回收
- V8使用分代式垃圾回收来管理内存,它将内存分为新生代和老生代
- 新生代:存活时间短的对象,使用Scavenge算法回收,速度快
- 老生代:存活时间长的对象或者从新生代晋升过来的对象,使用 “标记-清除”和“标记-整理”算法回收,避免内存碎片
理解了这个机制,有助于排查内存泄漏问题。我们知道垃圾回收是“停止世界”的,所以V8也在不断优化,比如并行、增量回收来减少对主线程的阻塞
V8 如何关联到实际开发?
1、优化对象属性:因为V8会将对象的属性存储在隐藏类中,所以如果属性数量过多,就会导致隐藏类膨胀,影响性能。所以在开发中,要在构造函数中一次性的初始化所有的对象属性,避免在运行时动态添加属性.这能保证V8的隐藏类不会膨胀,对对象的结构预测更准确,从而优化属性的访问速度
2、函数优化:在开发中会有意的编写单一类型、可预测的函数,比如一个函数的参数最好始终保持同一种数据类型,这样更有利于turbofan优化编译器的优化
3、规避”优化反悔“:v8的优化是基于假设的。比如,我写一个函数function add(a, b) {return a+b;},如果100次调用传递的都是数字,furbofan会将其优化未数字加法。但如果第101次传递了字符串,这个优化就会”失效“,v8不得不退回到解释执行,这样性能会下降。因此在代码种保持变量类型的稳定非常重要
4、内存管理: 在开发中,要避免频繁的创建和销毁对象,因为这会导致隐藏类的频繁创建和销毁,从而影响性能。还有要避免隐式全局变量,并且需要定时清除无用的定时器和事件监听
总结
简单来说,V8是chrome和node.js的高性能js引擎,它的核心职责是把js代码编译成机器码来执行。它实现高性能的关键是JIT即时编译,代码首先被解析成AST,然后被Igtition解释器转换成字节码来执行,解释过程中,如果发现某个函数被多次调用,那么它就会被标记为热点函数,Turbofan这个优化编译器就会登场,然后被编译成高效的机器码,下次再调用这个函数时,就直接运行机器码,速度极快。
在这个过程中,有两个非常重要的机制: 一是隐藏类和内联缓存,它们通过预测对象的结构和属性访问来优化性能。 二是垃圾回收,它通过分代式垃圾回收来管理内存,并通过并行、增量回收来减少对主线程的阻塞。