Skip to content

执行引擎作用与工作过程

执行引擎:字节码到机器码的最后一公里

执行引擎是 JVM 的「最后一公里」——它把方法区中的字节码指令转换成当前机器的本地机器码,然后交给 CPU 执行。这是 Java「一次编译,到处运行」的最后一环。

执行引擎的位置

执行引擎位于 JVM 的核心位置,连接了运行时数据区和物理硬件:

┌─────────────────────────────────────────────┐
│                  Java 源文件                  │
│                                             │
│              javac 编译器                    │
│                                             │
│                     ▼                       │
│                  字节码 (.class)            │
│                                             │
│          Class Loader(类加载器)            │
│                     │                        │
│                     ▼                        │
│           ┌─────────────────────┐           │
│           │    运行时数据区        │           │
│           │  堆 / 栈 / 方法区     │           │
│           └──────────┬────────────┘           │
│                      │                        │
│                      ▼                        │
│           ┌─────────────────────┐           │
│           │      执行引擎         │           │
│           │                      │           │
│           │  解释器(Interpreter) │           │
│           │  JIT 编译器           │           │
│           │  GC 线程              │           │
│           └──────────┬────────────┘           │
│                      │                        │
│                      ▼                        │
│               本地机器码                      │
│                     │                        │
│                     ▼                        │
│            操作系统 + 物理 CPU                │
└─────────────────────────────────────────────┘

执行引擎的两种执行方式

方式一:解释执行

解释器逐条读取字节码指令,翻译成机器码后立即执行。边翻译边执行,不需要预先编译。

字节码:iconst_1, iconst_2, iadd
        ↓        ↓        ↓
      机器码   机器码   机器码
        │        │        │
        ▼        ▼        ▼
      执行      执行      执行

优点:启动快(不需要编译),内存占用小。

缺点:每次执行都需要翻译,速度慢。

方式二:编译执行(JIT 编译)

Just-In-Time 编译器把热点字节码一次性编译成本地机器码并缓存,以后再执行时直接用编译后的代码。一次编译,多次执行

字节码:iconst_1, iconst_2, iadd

        │ (热点代码被 JIT 编译器捕获)

    一次性编译成本地机器码



    缓存到 Code Cache



    直接执行机器码(跳过解释)

优点:执行速度快,接近原生代码。

缺点:编译需要时间(编译开销),占用额外内存。

两种方式的对比

维度解释执行JIT 编译执行
启动速度快(无需编译)慢(需要预热)
执行速度慢(每次翻译)快(编译后直接执行)
内存占用大(Code Cache)
适用场景短期程序、不常用代码长期运行、热点代码
代表技术解释器HotSpot JIT、C2 编译器

执行引擎的完整工作流程

Java 程序启动


类加载(Class Loader)


执行引擎初始化

      ├── 解释器启动(立即开始执行)

      └── JIT 编译器就绪(后台编译热点代码)


主线程开始执行字节码

      ├── 解释执行字节码

      ├── JIT 编译器后台编译热点代码
      │         │
      │         └── Code Cache 存储编译后代码

      └── GC 线程并发执行


程序结束 / JVM 退出

解释器与 JIT 编译器并存

HotSpot VM 使用的是「解释器 + JIT 编译器」的混合模式:

字节码 → ┌──────────────────────────┐
         │        执行引擎            │
         │                           │
         │  ┌────────────────────┐   │
         │  │     解释器         │   │
         │  │  逐条翻译,即时执行 │   │
         │  └─────────┬──────────┘   │
         │            │               │
         │            │ (热点检测)    │
         │            ▼               │
         │  ┌────────────────────┐   │
         │  │   JIT 编译器        │   │
         │  │  编译热点,缓存代码  │   │
         │  └─────────┬──────────┘   │
         │            │               │
         │            ▼               │
         │     本地机器码              │
         │     (直接交给 CPU)         │
         └────────────┬───────────────┘


                 CPU 执行

为什么需要两者并存?答案很直接:

  • 解释器负责快速启动,不需要等待编译
  • JIT 编译器负责让长期运行的代码达到最高性能

两者的结合,让 JVM 既有良好的启动速度,又有出色的峰值性能。

执行引擎的内部组成

执行引擎内部

  ├── 解释器(Interpreter)
  │     ├── 字节码解释器(bytecode interpreter)
  │     └── 模板解释器(template interpreter)

  ├── JIT 编译器(JIT Compiler)
  │     ├── C1 编译器(Client Compiler)
  │     ├── C2 编译器(Server Compiler)
  │     └── Graal 编译器

  ├── 热点探测器(HotSpot Detector)
  │     ├── 方法调用计数器
  │     └── 回边计数器

  ├── 代码缓存(Code Cache)
  │     └── 存储编译后的本地机器码

  └── GC 线程(与执行引擎并发运行)

本节小结

执行引擎的核心要点:

关键点说明
定位字节码 → 本地机器码的转换器
解释执行边翻译边执行,启动快但速度慢
编译执行一次编译多次执行,速度快但有编译开销
混合模式解释器 + JIT 编译器,取长补短
热点探测识别高频调用的代码,只编译热点

理解执行引擎的工作原理,是理解 JVM 性能优化的入口。下一节,我们来看 编译 vs 解释运行(机器码/指令/汇编)

基于 VitePress 构建