V8 引擎运行 JS代码 — 解释&编译

905 阅读17分钟

V8 引擎运行 JS代码 — 解释&编译

V8 引擎是 JS 运行引擎中的一种,在浏览器和node中比较常见。新开V8引擎系列文章,研究探索引擎背后的调用流程和设计思路。V8引擎系列文章主要参考V8引擎开源源码,辅助开源社区资料(部分资料已经过时)进行总结。如有总结错误之处,望不吝赐教,在此拜谢。

前置-背景

什么是 V8引擎

V8 是一个C++编写的程序,它用于编译执行JavaScript代码。提供以下基础能力:

💡
1、编译并执行JS代码

2、以某种顺序执行函数,处理调用栈

3、管理对象的内存分配,堆内存

4、垃圾回收

5、提供所有 JS 语言支持的数据类型、运算符、API 和公共函数


V8 提供可选事件循环(浏览器中运行JS时,事件循环由浏览器提供)。V8 是一个单线程运行JS代码的多线程应用,V8引擎本身是多线程程序,V8采用单线程运行JS代码。

💡

V8运行JS代码是是单线程。一个V8实例,运行一个单独的JS执行上下文。在浏览器或者 node.js 开发的进程中可以同时存在多个V8线程实例来实现多线程并发,比如:通过WebWorker等技术开辟新的执行JS上下文并调用V8线程执行。

V8 引擎内部多线程大体划分为几类:

💡

1、主线程:负责执行 JS 代码解析、编译、运行。处理所有关于 JS 代码执行相关的任务,包括调用栈管理和事件循环。

2、垃圾回收线程:负责自动管理内存。

3、TurboFan 编译线程: V8 执行JS代码过程中会标记 hot 代码,TurboFan 线程会将 hot 代码编译优化,通常hot代码会被转换为机器码。

4I/O线程:node.js 环境中与 libuv 集成。负责文件系统操作,网络请求等等I/O操作。

5、其他辅助线程:debug、性能分析等等

V8 引擎运行 JS 代码流程大体如下:

image.png V8 只负责执行 JS,部分运行环境需要调用宿主提供。

Host Env 运行环境

V8的运行宿主环境有多种,最为常见的是浏览器 Browser 和 NodeJS 。Browser 环境和 NodeJS 环境有相同的成员如:ECMAScript standard (JavaScript核心内置API)、函数调用栈、Heap 内存、垃圾回收等等。不一致的在于特殊 API + 事件循环。

特殊点如下:

Browser 环境:

💡

**Web API**:  浏览器提供的接口,用于程序和浏览器交互 CanvasServiceWorkerWebStorageFetchAudio/VideoGeolocation等等

**DOM API**:文档对象模型,用于对 HTML 进行操作。主要包含以下方面 API 功能Element CRUDEvent Handing 事件监听处理、CSSOM 样式表修改 

**Event Loop**: 浏览器单独提供的时间循环与渲染周期结合

NodeJS 环境:

💡

**文件系统 API**: fs 模块,解决 i/o 读写

**网络 API**:http、https、net等模块

**环境变量 proces** :环境变量维护

**Cluster集群和子线程**:多线程并发API

**其他模块**: C++ 模块 

**Event Loop**:NodeJS 的 Event Loop采用了多阶段设计,允许更细粒度的控制

image 1.png 事件驱动模型中唤起的回调,经过事件循环调用V8引擎执行。页面事件/浏览器生命周期/WebAPI 等等来源触发的回调函数,被添加到事件循环的任务队列中【这里也可称之为宏任务队列】。事件循环与微任务队列的关系在本文中不会深入,会单开一文。

以浏览器环境调用V8运行JS为例

image 2.png

到此,正式进入 JS 代码如何在引擎中运行过程。第一步是:解释执行编译优化

编译 & 运行

过程

V8 引擎将JS代码编译转换成字节码和部分机器码。步骤如下:

💡
1. 解析器将 JS Code 解析转换成 AST 并分析出关联的 Scopes 作用域, 构建AST 的过程称之为解析 Parse。
2. 随后进行第一次编译 AST + Scopes 编译转换成字节码, 第一个编译器名字叫做 Ignition ,ignition 将 AST 转变成 bytecode 字节码。
3. 解释执行字节码。字节码被解释器 inceptor 解释执行,解释执行过程中会进行代码优化标记,。
4. IC 优化。
5.  Hot 代码三级优化,转成机器码。针对Hot代码,V8引擎目前采用三级优化模式。分别是:Sparkplug、Maglev、TurboFan 。三位大师负责将标记为 Hot 代码进一步编译成为机器码,以此提升代码运行速度。机器码被 CPU 直接运行。

image 3.png

到了这里会有两个疑问,为什么不全部编译成为机器码?第二什么情况下会进行优化,什么情况下会退出优化?

💡

- **最快的启动运行和不太大的内存。**
    - JIT 编译成为字节码能够节约时间,直接编译成机器码需要分析整个JS脚本推断所有对象类型和优化条件。Web 站点快速加载、响应流畅需要最快启动JS脚本执行。
    - 大部分JS代码不会重复运行,运行初始编译成机器码耗时多且内存占用非常大。
- **JS 语言特性:动态类型不确定性**
    - 运行时频繁修改变量类型,未运行前无法推断准确的类型。
    - 通过解释器 Ignition 运行字节码时,标记变量类型结构固定且频繁执行的 代码为 Hot,将Hot代编译成码机器码提升运行速率。
- **跨平台兼容性**
    - bytecode 与平台无关,可以在不同的硬件架构上运行。机器码由 CPU 直接运行,与 CPU 架构以及支持的编码强相关。

字节码 VS 机器码

编译有两种常见的模式 JIT (just in time)即时编译和 AOT(Ahead of time)提前编译。JIT 是在代码运行期间动态编译,编译-运行-编译-运行。AOT 一般常见于C++ 和 JAVA 这类强类型语言,打包构建时直接产出字节码或机器码。V8 引擎编译JS代码采用的是 JIT编译模式。

V8 执行 JS 代码过程中先翻译成 bytecode, 多次运行会标记可被优化的代码标记为 Hot TuboFan 将 Hot 代码从 bytecode 编译成机器码,加速运行【CPU直接运行机器码】。

image 4.png

V8 提供的字节码映射表:

github.com/v8/v8/blob/…

10k 的 JS 代码就全编译成机器码需要 20M 的空间,1M的JS代码则需要约 2G 的内存空间。

image.png

V8采用折中方案:解释执行大部分代码,对部分高频代码进行优化。

V8 引擎运行优化

V8 引擎优化 “在内存、CPU、等资源合适的情况下,优化调用频繁;类型结构稳定;执行时间长;代码规模适中;代码控制流程简单的代码” 提升运行效率。剔除死代码,死代码包括:不可达代码;无用计算;冗余的类型检查。

在分析优化逻辑之前,需要先介绍一下 feedback_vector 。JS 代码在V8引擎中运行过程中,所有的代码分析 / 运行记录 等信息都是记录在 feedback_vector 上。

image 5.png

JS source → bytecode -> intercept execute - … → Maglev … → TurboFan → MachineCode .. 过程中代码的分析结果,运行中的返回值、稳定性、耗时等信息都存储在 feedback_vector 上.

💡

分类介绍 feedback_vector 存储的重要信息

1、代码编译分析信息:

2、运行时优化信息

V8 基于 各个环节提取的 feedback_vector 信息进行优化/去优化分析。包括:死代码消除、IC 内联代码优化、Hot 标记优化。

标记优化的大流程如下:

image 6.png

死代码消除

V8 对代码进行标记,不可达的代码;无用计算的代码;冗余的类型检查等等代码会被标记为 isDead。这些代码不会被执行也不会被内联优化检查到!

内联优化 【src/compiler/js-inlining-heuristic.cc】

减少函数调用开销(调用栈切换);减少局部变量访问次数来提升实现优化。函数体积小、调用频率高、参数类型稳定、没有复杂控制流。一句话,高频调用且可预测的小函数。

内联优化的小 case

// 原代码
function add(a, b) {
  return a + b;
}
function calc(x, y) {
  return add(x, y) * 2; 
}

// 内联优化后
function calc(x, y) {
  return (x + y) * 2;
}

内联优化标记策略:

💡

1. 构造函数调用或者普通函数调用
2. 存在直接递归调用的不优化
3. 未达到最低调用频率不优化
4. 强制内联小函数【27字节码以内,转换JS代码大概1-2行】注意:即使是小函数不符合前三条也不会优化!
5. 考虑内存和性能预算,当内存预算到了瓶颈停止内联,内存占用超过预算甚至会将部分内联进行退化。小函数优势在此!
// file: src/compiler/js-inlining-heuristic.cc
Reduction JSInliningHeuristic::Reduce(Node* node) {

  // 非构造函数调用或者普通函数调用 退出检查
  if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();

	// 累计内联大小超过上限--退出
  if (total_inlined_bytecode_size_ >= max_inlined_bytecode_size_absolute_) {
    return NoChange();
  }

  ...  
  for (int i = 0; i < candidate.num_functions; ++i) {
  
  
	  // 直接递归调用---不优化
    if (frame_info.shared_info().ToHandle(&frame_shared_info) &&
        frame_shared_info.equals(shared.object())) {
      TRACE("Not considering call site #" << node->id() << ":"
                                          << node->op()->mnemonic()
                                          << ", because of recursive inlining");
      candidate.can_inline_function[i] = false;
    }
 
	 // 强制优化小函数
    if (candidate.can_inline_function[i]) {
      can_inline_candidate = true;
      BytecodeArrayRef bytecode = candidate.bytecode[i].value();
      candidate.total_size += bytecode.length();
      unsigned inlined_bytecode_size = 0;
      if (OptionalJSFunctionRef function = candidate.functions[i]) {
        if (OptionalCodeRef code = function->code(broker())) {
          inlined_bytecode_size = code->GetInlinedBytecodeSize();
          candidate.total_size += inlined_bytecode_size;
        }
      }
      candidate_is_small = candidate_is_small &&
                           IsSmall(bytecode.length() + inlined_bytecode_size);
    }
  }
  
  if (!can_inline_candidate) return NoChange();

	// 未达到最低调用频率不优化:min_inlining_frequency = 0.15
  if (candidate.frequency.IsKnown() &&
      candidate.frequency.value() < v8_flags.min_inlining_frequency) {
    return NoChange();
  }

  seen_.insert(node->id());

  // 小函数强制内联---
  if (candidate_is_small) {
    return InlineCandidate(candidate, true);
  }

  // In the general case we remember the candidate for later.
  candidates_.insert(candidate);
  return NoChange();
}
  // Returns true if opcode can be inlined.
  static bool IsInlineeOpcode(Value value) {
    return value == kJSConstruct || value == kJSCall;
  }
💡

opcode 常见值如下:

1. **kJSCall**:表示一个普通的 JavaScript 函数调用。
2. **kJSConstruct**:表示一个构造函数调用,通常是通过 **`new`** 关键字调用的函数。
3. **kJSReturn**:表示返回语句,结束当前函数并返回值。
4. **kJSLoadProperty**:表示加载对象的属性。
5. **kJSStoreProperty**:表示将值存储到对象的属性中。
6. **kJSLoadElement**:表示加载数组或类数组对象的元素。
7. **kJSStoreElement**:表示将值存储到数组或类数组对象的元素中。
8. **kJSThrow**:表示抛出异常。
9. **kJSBranch**:表示条件分支或跳转。

标记完成后,需要进行优化前检查,排序。到这里的基本都是非小函数【大概是函数体超过2行,小函数字节码限制27字节】。

// file: src/compiler/js-inlining-heuristic.cc
// 排序策略
void JSInliningHeuristic::Finalize() {

...
  while (!candidates_.empty()) {
    auto i = candidates_.begin();
    Candidate candidate = *i;
    candidates_.erase(i);

    // 已经优化了的--跳过
    if (!IrOpcode::IsInlineeOpcode(candidate.node->opcode())) continue;
    
    // 无效死代码,跳过
    if (candidate.node->IsDead()) continue;

    // 预算检查---
    double size_of_candidate =
        candidate.total_size * v8_flags.reserve_inline_budget_scale_factor;
    int total_size =
        total_inlined_bytecode_size_ + static_cast<int>(size_of_candidate);
    
    // 超过内联预算上限---本次暂时不优化
    if (total_size > max_inlined_bytecode_size_cumulative_) {
      info_->set_could_not_inline_all_candidates();
      // Try if any smaller functions are available to inline.
      continue;
    }

		// 恭喜!!进入下一环节~ 
    Reduction const reduction = InlineCandidate(candidate, false);
    if (reduction.Changed()) return;
  }
}

分析函数历史执行情况!从参数格式结构稳定性、运行结果稳定性、异常错误等方面进行评估,内容来自 feedback_vector。

  • 稳定性检查,出现过不稳定执行的函数中止优化!
  • 函数内逐步进行内联优化!直到总内联优化占用达到上限!
Reduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate,
                                               bool small_function) {
   ...                                            
  // 检查调用是否稳定--参数检查!
  Node* if_exception = nullptr;
  if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
    Node* if_exceptions[kMaxCallPolymorphism + 1];
    
    // 检查历史执行情况:运行稳定性、异常结果检查
    for (int i = 0; i < num_calls; ++i) {
      if_successes[i] = graph()->NewNode(common()->IfSuccess(), calls[i]);
      if_exceptions[i] =
          graph()->NewNode(common()->IfException(), calls[i], calls[i]);
    }
    // Morph the {if_exception} projection into a join.
    ...
  }

...

  // 稳定性检查通过后,逐步进行内联优化!直到总内联优化占用达到上限!
  for (int i = 0; i < num_calls && total_inlined_bytecode_size_ <
                                       max_inlined_bytecode_size_absolute_;
       ++i) {
    if (candidate.can_inline_function[i] &&
        (small_function || total_inlined_bytecode_size_ <
                               max_inlined_bytecode_size_cumulative_)) {
      Node* call = calls[i];
      Reduction const reduction = inliner_.ReduceJSCall(call);
      if (reduction.Changed()) {
        total_inlined_bytecode_size_ += candidate.bytecode[i]->length();
        call->Kill();
      }
    }
  }

  return Replace(value);
}

内联优化存在几个针对字节码大小的限制,

💡

- 小函数内联大小:27字节
- 单个内联最大字节码:460字节
- 累计内联上限:920字节
// file : src/flags/flag-definitions.h
DEFINE_INT(max_inlined_bytecode_size, 460,
           "maximum size of bytecode for a single inlining")
DEFINE_INT(max_inlined_bytecode_size_cumulative, 920,
           "maximum cumulative size of bytecode considered for inlining")
DEFINE_INT(max_inlined_bytecode_size_absolute, 4600,
           "maximum absolute size of bytecode considered for inlining")
DEFINE_FLOAT(
    reserve_inline_budget_scale_factor, 1.2,
    "scale factor of bytecode size used to calculate the inlining budget")
DEFINE_INT(max_inlined_bytecode_size_small, 27,
           "maximum size of bytecode considered for small function inlining")
DEFINE_INT(max_optimized_bytecode_size, 60 * KB,
           "maximum bytecode size to "
           "be considered for turbofan optimization; too high values may cause "
           "the compiler to hit (release) assertions")
DEFINE_FLOAT(min_inlining_frequency, 0.15, "minimum frequency for inlining")
DEFINE_BOOL(polymorphic_inlining, true, "polymorphic inlining")

其他不会被优化的场景:

💡

- 动态的 eval
- with 语句
- 复杂的 try catch
- 单个函数编译为字节码内存超过 460 字节
- 调用评率低:ignition 为 0.15 [单位没看懂]
- 动态 import
- 非纯函数,存在不可预测的过程

内联优化是通过对解释执行的字节码进行执行过程中分析转换的,内联优化后产物还是字节码,Hot 代码中内联优化的代码则会一同编译成机器码。Hot 代码编译机器码部分,不再追溯关于内联优化相关内容。

Hot 代码优化

V8 代码在字节码解释执行阶段会附带标记Hot代码(附带信息存于feedback_vector),通过将 Hot 代码从字节码编译成机器码。机器码由CPU直接运行效率远远大于ignition解释器运行字节码。

V8 引擎采用多级优化策略(平衡编译耗时和机器码优化深度),截止24年11月,V8发布源码中采用的是三级编译器。 Maglev 编译器第一版本发布是在 chrome 117 。

先介绍一下Hot代码涉及的优化编译器:

image 7.png

三级优化模型,V8目的是平衡编译成本和运行速度。编译成本:编译时间 + 内存占用。编译速度Sparkplug最快。以下是编译的速度:

image 8.png

V8 引擎解释器+编译器优化历史组合运行性能跑分情况:

image 9.png

💡

### 1. **Ignition**(解释器)

- **阶段**:Ignition 是 V8 的字节码解释器,它是 JavaScript 执行的第一个阶段。
- **功能**:将 JavaScript 源代码编译为字节码,并逐条解释执行。这种解释执行通常适合初始化阶段或短期、低频执行的代码。
- **调用条件**:代码首次加载时,Ignition 会将源代码转为字节码,然后解释执行。
- **优点**:解释器生成字节码的速度较快、内存开销小,但由于每次都需要解释,性能会比编译的机器码稍慢。

### 2. **Sparkplug**(基线编译器)

- **阶段**:在 Ignition 之后,作为快速编译阶段执行。
- **功能**:Sparkplug 是 V8 的基线编译器,负责将字节码快速编译为机器码,来提高代码执行速度。相比解释执行,编译后的代码运行更快,但 Sparkplug 并不会进行复杂优化,因此编译时间也非常短。
- **调用条件**:当代码被标记为“热”代码; 不存在 feedback_vector;
- **优点**:快速生成机器码,提升代码性能的同时仍保持较低内存消耗。

### 3. **Maglev**(中层编译器)

- **阶段**:位于 Sparkplug 和 TurboFan 之间。
- **功能**:Maglev 是一种中层即时编译器,用于在代码“热度”增加但尚未进入深度优化阶段时提供进一步的优化。Maglev 生成的机器码质量高于 Sparkplug,但没有 TurboFan 的复杂优化。
- **调用条件**:Sparkplug 优化代码执行优化后,代码类型允许升级到 Maglev;之前没有Maglev 编译失败记录;未启动PGO(Profile Guided Optimization),启动PGO的直奔TurboFan;
- **优点**:提供更高质量的机器码,在提高性能的同时避免过多调用 TurboFan。

### 4. **TurboFan**(优化编译器)

- **阶段**:在代码被频繁调用后,为性能的最后提升执行。
- **功能**:TurboFan 是 V8 的优化编译器,对代码进行深度优化,生成高度优化的机器码。它适合执行频率非常高、长时间运行的“热”代码。
- **调用条件**:代码执行频率极高时触发,特别是在 Maglev 生成的代码仍不够高效的情况下,才会进入 TurboFan 的深度优化阶段。
- **优点**:TurboFan 会观察代码的执行模式,通过内联缓存和隐藏类等信息,对代码进行进一步优化,使其达到最佳性能。
// file: src/execution/tiering-manager.cc
void TieringManager::OnInterruptTick(DirectHandle<JSFunction> function,
                                     CodeKind code_kind) {
	... 
  // 第一步:先检查是否可使用 Sparkplug 优化:【函数没有feedback_vector】
  const bool compile_sparkplug =
      CanCompileWithBaseline(isolate_, function->shared()) &&
      function->ActiveTierIsIgnition(isolate_) && !maybe_had_optimized_osr_code;

  if (compile_sparkplug) {
#ifdef V8_ENABLE_SPARKPLUG
    // sparkpug 入口。。。
    if (v8_flags.baseline_batch_compilation) {
      isolate_->baseline_batch_compiler()->EnqueueFunction(function);
    }

  // 
  if (first_time_tiered_up_to_sparkplug) {
    if (had_feedback_vector) {
      if (function->shared()->cached_tiering_decision() ==
          CachedTieringDecision::kPending) {
        function->shared()->set_cached_tiering_decision(
            CachedTieringDecision::kEarlySparkplug);
      }
      function->SetInterruptBudget(isolate_);
    }
    return;
  }

	// Sparkplug 优化后升级的场景:Maglev、turboFan
  MaybeOptimizeFrame(function_obj, code_kind);

 ...
}

//----------------------------------------------------------

void TieringManager::MaybeOptimizeFrame(Tagged<JSFunction> function,
                                        CodeKind current_code_kind) {

... 

  if (V8_UNLIKELY(v8_flags.always_osr)) {
    TryRequestOsrAtNextOpportunity(isolate_, function);
    // Continue below and do a normal optimized compile as well.
  }

  const bool maglev_osr = maglev::IsMaglevOsrEnabled();
  const CodeKinds available_kinds = function->GetAvailableCodeKinds(isolate_);
  
  const bool waiting_for_tierup =
      (current_code_kind < CodeKind::TURBOFAN_JS &&
       (available_kinds & CodeKindFlag::TURBOFAN_JS)) ||
      (maglev_osr && current_code_kind < CodeKind::MAGLEV &&
       (available_kinds & CodeKindFlag::MAGLEV));
  
  
  if (function->IsOptimizationRequested(isolate_) || waiting_for_tierup) {
    // 节能模式或电池节省模式 --- 不优化
    if (V8_UNLIKELY(maglev_osr && current_code_kind == CodeKind::MAGLEV &&
                    (!v8_flags.osr_from_maglev ||
                     isolate_->EfficiencyModeEnabledForTiering() ||
                     isolate_->BatterySaverModeEnabled()))) {
      return;
    }

    // OSR kicks in only once we've previously decided to tier up, but we are
    // still in a lower-tier frame (this implies a long-running loop).
    // 需要优化:但是优先级不够的,提供优化:大循环
    TryIncrementOsrUrgency(isolate_, function);
    return;
  }

  OptimizationDecision d =
      ShouldOptimize(function->feedback_vector(), current_code_kind);
      
  // We might be stuck in a baseline frame that wants to tier up to Maglev, but
  // is in a loop, and can't OSR, because Maglev doesn't have OSR. Allow it to
  // skip over Maglev by re-checking ShouldOptimize as if we were in Maglev.
  if (V8_UNLIKELY(!isolate_->EfficiencyModeEnabledForTiering() && !maglev_osr &&
                  d.should_optimize() && d.code_kind == CodeKind::MAGLEV)) {
    bool is_marked_for_maglev_optimization =
        existing_request == CodeKind::MAGLEV ||
        (available_kinds & CodeKindFlag::MAGLEV);
    // 优化第二级:Maglev 优化检查进入
    if (is_marked_for_maglev_optimization) {
      d = ShouldOptimize(function->feedback_vector(), CodeKind::MAGLEV);
    }
  }

	// 进入终极优化: turboFan
  if (d.should_optimize()) Optimize(function, d);
}

//-------------------------------

OptimizationDecision TieringManager::ShouldOptimize(
    Tagged<FeedbackVector> feedback_vector, CodeKind current_code_kind) {
  Tagged<SharedFunctionInfo> shared = feedback_vector->shared_function_info();
  
  // 已经优化过的代码,不处理
  if (current_code_kind == CodeKind::TURBOFAN_JS) {
    return OptimizationDecision::DoNotOptimize();
  }

  if (TiersUpToMaglev(current_code_kind) &&
      shared->PassesFilter(v8_flags.maglev_filter) &&
      !shared->maglev_compilation_failed()) {
    if (v8_flags.profile_guided_optimization &&
        shared->cached_tiering_decision() ==
            CachedTieringDecision::kEarlyTurbofan) {
      // 进入TurboFan 优化
      return OptimizationDecision::TurbofanHotAndStable();
    }
    
    // Maglev 优化
    return OptimizationDecision::Maglev();
  }

  if (V8_UNLIKELY(!v8_flags.turbofan ||
                  !shared->PassesFilter(v8_flags.turbo_filter) ||
                  (v8_flags.efficiency_mode_disable_turbofan &&
                   isolate_->EfficiencyModeEnabledForTiering()) ||
                  isolate_->BatterySaverModeEnabled())) {
    return OptimizationDecision::DoNotOptimize();
  }

	// invocation_count 函数的调用次数!!efficiency_mode_delay_turbofan 初始值15000
  if (isolate_->EfficiencyModeEnabledForTiering() &&
      v8_flags.efficiency_mode_delay_turbofan &&
      feedback_vector->invocation_count() <
          v8_flags.efficiency_mode_delay_turbofan) {
    return OptimizationDecision::DoNotOptimize();
  }

	// 优化的字节码超过上限:不优化 max_optimized_bytecode_size 初始值60KB
  Tagged<BytecodeArray> bytecode = shared->GetBytecodeArray(isolate_);
  if (bytecode->length() > v8_flags.max_optimized_bytecode_size) {
    return OptimizationDecision::DoNotOptimize();
  }

	// 进入 TurboFan 优化 
	return OptimizationDecision::TurbofanHotAndStable();
}

以上就是Hot代码分层优化的内容,什么时候去优化呢?当内存资源出现压力后、函数变冷(调用不再频繁)等情况优化会被去掉,退回字节码解释执行。

如何写好代码?

思考题~

参考资料

💡

- [Maglev - V8最快的JIT优化](https://v8.dev/blog/maglev)  https://v8.dev/blog/maglev
- [how-v8-javascript-engine-works](https://cabulous.medium.com/how-v8-javascript-engine-works-5393832d80a7) https://cabulous.medium.com/how-v8-javascript-engine-works-5393832d80a7
- [Franziska Hinkelmann: JavaScript engines - how do they even? | JSConf EU](https://www.youtube.com/watch?v=p-iiEDtpy6I) [https://www.youtube.com/watch?v=p-iiEDtpy6I](https://www.youtube.com/watch?v=p-iiEDtpy6I)
- V8 [github.com/v8](https://github.com/v8/v8)