V8引擎为啥这么快
函数内联(InLining)
🚀 V8引擎的内联机制,是一切优化的基础。
比如有一个封装的函数,在任意位置调用,V8会直接将函数调用替换为该函数的实际代码。
function func1(a, b) { return a + b; } function func2(x, y) { return func1(x, y) + 10; }
🫡 会被直接转换为:
function func2(x, y) { return x + y + 10; }
当然,触发内联机制机制也是有条件,主要有以下要求:
- 不能有
arguments
、try-catch
、with
语法、参数。
- try-catch,虽然新版V8优化了该问题,但是还是会影响性能。👉 深入了解
- 简短的函数。
- 这也是“高内聚、低耦合”的概念,不要把很多逻辑全部塞在一起,V8也难以解偶优化哦!
- 函数被频繁调用。
隐藏类(Hidden Class)
💡C++、Java这类语言每个变量,都会有一个唯一确定的类型。在编译阶段就可以确定对象中的
偏移量
等信息,CPU就可以直接调用对象首地址
(C++中是this指针)。
JavaScript是动态语言,变量可以随时被任意类型对象赋值,对象本身也可以随时增加、删除成员。访问对象属性需要的信息,完全由运行时确定。为了按索引的方式访问成员,🚀 V8引擎内部实现了对象分类。隐藏类是V8内部的一种数据结构,本身也属于一个对象。下面给一个例子:
// 调用 Point 会创建一个 “引隐藏类” C0 function Point(x, y) { // 基于 C0(Point) 变量x 被创建成C1 ,C1描述了找到X属性在内存中的位置(相当于this) this.x = x; // 这里的 y 会被创建成 C2 this.y = y; } // 🎉 隐藏类转换性能,取决于属性添加的顺序,顺序不同,创造的隐藏类不同。 var p1 = new Point(1, 2); p1.a = 5; p1.b = 6; // 🎉 如p2 也能跟p1一样赋值a、b 则性能最优 var p2 = new Point(3, 4); p2.b = 7; p2.a = 8;
内联缓存(Inline Caching)
🚀 V8对象访问过程: 获取
隐藏类地址
➡ 通过属性名查找偏移值
➡计算属性地址
JavaScript是动态语言,JIT运行过程中,对象的属性访问,会反复执行。内联缓存功能,就是把执行访问过程(隐藏类
、偏移值
、属性地址
)给缓存起来。
内联缓存功能,大致思路,就是将初次查找的隐藏类、偏移值保存起来,下次查找,先比较当前对象是否是之前的隐藏类,如果不是就重新创建隐藏类、查找偏移值、计算属性地址的逻辑。
函数内联(Machine Code)
🚀 V8为了提升JavaSctipt执行效率,编译器直接生成机器码(优化回退)。
JavaScript在运行过程中,会采集函数的执行次数。若频繁执行
,将会被V8引擎标记为热点函数
。
针对热点函数
,V8会直接编译成机器码。若遇到类型变化,🚀V8将会退回编译成机器字节码。
垃圾回收机制 (GC)
🚀 V8引擎的垃圾回收机制、算法,是当今 JavaScript 性能优化的核心。同时,也是 “空间换时间” 的典型用例。
简单来说, V8引擎通过 分代管理
、增量并发
算法机制,在保证垃圾回收效率的同时,提升了 JavaScript 的执行效率。
网络上关于 JavaScript 垃圾回收的文章太多太多,简单几句话也聊不完,我单开一篇文章,与大家详聊。