V8

Google 开发的 JavaScript 引擎,负责解析、编译、执行和优化 JavaScript,并管理运行期内存与垃圾回收。

#type / resource #status / growing #tech / dev #resource / v8 #resource / javascript #platform / browser #platform / server

[!info] related notes

V8

这是什么

V8 是 Google 开发的 JavaScript 引擎。

它的核心职责是:

  • 解析 JavaScript 源码
  • 把代码转换成可执行形式
  • 执行并优化热点代码
  • 管理对象、函数、闭包等运行期内存
  • 负责垃圾回收

如果只用一句话概括:

V8 是 JavaScript 代码真正被“跑起来”的执行核心。

适用平台

  • Chrome 等基于 Chromium 的浏览器环境
  • Node.js 运行时
  • 其他嵌入式 JavaScript 宿主环境

V8 本身不是浏览器,也不是 Node.js。它是这些宿主环境内部的 JavaScript 执行引擎。

核心特点 / 优势 / 局限

核心特点

  • 把 JavaScript 从源码逐步转成字节码与机器码
  • 通过 Ignition 和 TurboFan 组合做执行与优化
  • 通过运行期信息优化热点代码
  • 管理堆内存、调用栈和垃圾回收
  • 既能服务浏览器,也能服务 Node.js

优势

  • 执行速度高,能对热点路径持续优化
  • 对现代 JavaScript 特性支持成熟
  • 自动做内存管理与垃圾回收
  • 很适合作为浏览器和服务端运行时的执行核心

局限

  • 引擎本身较重,启动和内存成本不算低
  • JavaScript 的动态类型会增加优化难度
  • GC 仍可能带来停顿和延迟抖动
  • 它只负责执行 JavaScript,不负责事件循环、DOM、文件系统或 HTTP

它和 Node.js、Chrome 的关系

最容易混淆的一点是:V8 不等于 Node.js,也不等于 Chrome。

在 Node.js 中

可以先粗略理解成:

Node.js = V8 + libuv + C++ bindings + Node 标准库

其中:

  • V8 负责执行 JavaScript
  • libuv 负责事件循环、线程池和异步 I/O 协调
  • Node API 暴露 fshttpstreamcrypto 等服务端能力

所以 fs.readFile() 背后的文件读取不是 V8 做的,但回调函数里的 JavaScript 逻辑仍然是 V8 在执行。

在 Chrome 中

Chrome 是完整浏览器,除了 V8 之外还包括:

  • 渲染引擎
  • DOM / BOM
  • 网络模块
  • 图形与页面渲染

因此 document.querySelector() 这样的 DOM API 不是 V8 发明的;V8 只是负责执行调用这些 API 的 JavaScript 代码。

它大致怎样执行一段 JavaScript

可以把 V8 的执行链路先记成下面这条主线:

JavaScript 源码

Parser

AST

Ignition 字节码

执行字节码

发现热点代码

TurboFan 优化编译

机器码

1. 解析源码

V8 先读入 JavaScript 源码,并检查语法结构。

这一步会形成 AST,也就是抽象语法树。AST 不是最终执行结果,而是“代码结构”的中间表示。

2. 生成字节码

V8 不会一开始就把所有代码都做重度优化,而是先通过 Ignition 生成字节码。

字节码可以理解成:

  • 比 JavaScript 更底层
  • 比机器码更容易生成
  • 启动更快,适合先运行起来

3. 优化热点代码

如果某段函数、循环或属性访问被频繁执行,V8 会把它视为热点代码,并收集运行期信息:

  • 调用次数
  • 参数类型
  • 对象结构是否稳定
  • 某个属性访问是否总落在同一种对象形状上

满足条件后,TurboFan 会把它进一步优化成更快的机器码。

JIT、热点代码与反优化

V8 的一个关键特征是 JIT,也就是即时编译。

它的思路不是“先把全部代码重编译完再跑”,而是:

  1. 先尽快执行
  2. 再观察哪些代码最常跑
  3. 对这些热点路径做更激进的优化

什么是热点代码

热点代码就是被频繁执行、值得额外投入优化成本的代码。

例如一个高频调用的函数、一个大循环里的属性访问,往往就会成为热点。

为什么还会反优化

V8 的优化建立在“运行期观察到的模式比较稳定”这个前提上。

如果它之前发现某函数总是处理数字,于是按数字路径优化;后来你又把字符串传进去,原来的假设可能失效,这时就会触发反优化,退回到更通用但更慢的执行方式。

这也是为什么很多性能经验都强调:

  • 变量类型尽量稳定
  • 热路径不要频繁改对象结构

V8 常见优化直觉

Hidden Class

JavaScript 对象对外看起来很灵活,但 V8 为了加速属性访问,会在内部为结构相近的对象建立隐藏类。

如果对象的属性集合和添加顺序长期稳定,V8 更容易优化属性访问;如果对象总是动态增删字段、顺序也不稳定,优化效果会变差。

Inline Cache

当某个属性访问路径反复出现,比如多次读取 user.name,并且 user 的对象形状稳定,V8 会缓存这类访问模式,避免每次都做完整查找。

对性能更友好的写法

  • 高频路径里尽量保持参数类型稳定
  • 尽量让对象结构稳定
  • 少在热路径里频繁 delete 属性
  • 少做不必要的大对象创建和销毁

这些建议不是“语法规则”,而是为了让 V8 更容易保留优化状态。

内存管理、调用栈与 GC

V8 不只是执行代码,也负责运行期内存管理。

调用栈

函数调用会进入调用栈。递归过深或无限递归时,常见报错就是:

Maximum call stack size exceeded

更基础的心智模型见 调用栈和执行上下文的关系

堆内存

对象、数组、函数等引用类型通常与堆内存管理更相关。堆不是“对象一定都在堆里”这种面试口号,而是用来理解运行期分配与回收的一个重要视角。

垃圾回收

当对象不再可达时,V8 才有机会回收它。

这也是为什么内存泄漏通常不是“忘记 free”,而是:

  • 全局缓存一直持有对象
  • 定时器或监听器没清理
  • 闭包长期持有大对象

更完整的 GC 与泄漏背景看 ECMAScript内存管理

V8 和事件循环不是一回事

这是 Node.js 面试里非常高频的混淆点。

V8 负责:

  • 解析 JavaScript
  • 编译和执行 JavaScript
  • 管理内存和 GC

但事件循环不是 V8 本身提供的。

  • 在 Node.js 里,事件循环主要来自 Node.js 运行时架构 里的 libuv
  • 在浏览器里,事件循环由浏览器宿主环境实现

所以 setTimeout、DOM、fetch、文件系统这些能力,都不是 ECMAScript 语言本体或 V8 单独提供的。

常见用途

  • 作为 Chrome 的 JavaScript 执行引擎
  • 作为 Node.js 的 JavaScript 执行核心
  • 为其他宿主环境提供可嵌入的 JavaScript 运行能力
  • 支撑现代前端工具链和服务端 JavaScript 生态

相关链接 / 官方入口

创建于 2026/5/21 更新于 2026/5/27