JVM-编译执行过程

java文件的编译过程

编译过程

  • 第一步:把.java源文件编译为.class字节码文件(也叫jvm指令集),与平台无关,这是第一次编译,生成的这个文件就是可到处运行的文件,流程如下图

  • 第二步:.class字节码文件到目标机器码,这个过程由JVM执行引擎来完成,是第二次编译。流程如下图

java程序能够一次编译到处执行的原因

  • 第二次编译是在JVM中执行的,而JVM适配了不同的操作系统,根据不同的操作系统解释成不同的机器码
  • 不同平台对应不同JVM,JVM会根据平台生成不同指令集
平台 安装的 JVM 生成的机器码
Windows x64 HotSpot for Windows x86-64 指令
Linux x64 HotSpot for Linux x86-64 指令
macOS ARM HotSpot for macOS ARM64 指令
Android ART / Dalvik ARM 指令

执行引擎:解释执行 vs JIT 编译

  • 首先,java是解释机制,不是编译机制,虽然java经过了两次编译,但是第二次编译是在解释适应不同平台,在第二次编译过程中,真正的采用解释机制,即翻译一句,执行一句,不产生整个的机器代码,翻译过程如果不报错,会一直执行
  • 但同一个程序,解释执行比编译执行的运行速度较慢,但对于java来说差别不大,在了解其原因前先了解下JVM 执行字节码的两种方式

1.解释执行

sql 复制代码
字节码 ──► 解释器逐条读取 ──► 翻译成机器码 ──► 立即执行
  • 优点:无需等待编译,直接解释成机器码,启动快
  • 缺点:每条指令都要翻译,执行慢

JIT即时编译(just-in-time complier)

sql 复制代码
热点代码(执行次数超过阈值)
    │
    ▼
┌─────────────┐
│  C1 编译器   │  ← 客户端编译器,优化简单,编译快
│  (-client)  │     适合桌面应用
└─────────────┘
    │
    ▼
┌─────────────┐
│  C2 编译器   │  ← 服务端编译器,深度优化,编译慢但执行快
│  (-server)  │     方法内联、逃逸分析、锁消除等
│  (Graal JIT)│
└─────────────┘
    │
    ▼
本地机器码(缓存到 Code Cache)

执行流程图

sql 复制代码
┌─────────────────┐
│   字节码文件      │
└─────────────────┘
         │
         ▼
┌─────────────────┐
│  解释器直接执行   │ ◄──── 冷代码(执行次数少)
│  (启动阶段)     │
└─────────────────┘
         │
         ▼  执行次数达到阈值
┌─────────────────┐
│  JIT 编译器介入   │
│  - C1 简单优化   │
│  - C2 深度优化   │
└─────────────────┘
         │
         ▼
┌─────────────────┐
│  生成本地机器码    │
│  存入 Code Cache │
└─────────────────┘
         │
         ▼
┌─────────────────┐
│  后续直接执行机器码 │ ◄──── 热点代码(执行次数多)
│  (不再解释字节码) │
└─────────────────┘

这就是java虽然是解释执行,但相比编译执行的运行速度不差多少,就是因为JVM有JIT机制和解释执行两种方式配合执行,JIT适合热点代码,执行快;解释执行适合冷代码,启动快,两者相互搭配。

JIT 编译是 Java 性能接近 C++ 的关键。它甚至能根据运行时信息做优化(如方法内联、逃逸分析),这是静态编译难以做到的。

且对于Java项目来说,当服务器持续处理请求时,JIT会持续优化,当热点代码被JIT 编译后,执行速度很快。

所以,Java服务需要预热

JDK JVM JRE IDE之间的关系

关系图

sql 复制代码
┌─────────────────────────────────────────┐
│              JDK (Java Development Kit)   │  ← 开发工具包
│  ┌─────────────────────────────────┐    │
│  │           JRE (Java Runtime Environment) │  ← 运行时环境
│  │  ┌─────────────────────────────┐ │    │
│  │  │        JVM (Java Virtual Machine) │  │  ← 虚拟机核心
│  │  │  ┌─────────────────────┐    │ │    │
│  │  │  │   核心类库 (rt.jar)  │    │ │    │
│  │  │  │   (String, List, IO) │    │ │    │
│  │  │  └─────────────────────┘    │ │    │
│  │  │  ┌─────────────────────┐    │ │    │
│  │  │  │   运行辅助工具        │    │ │    │
│  │  │  │   (java, javaw)      │    │ │    │
│  │  │  └─────────────────────┘    │ │    │
│  │  └─────────────────────────────┘ │    │
│  │  ┌─────────────────────────────────┐  │
│  │  │   扩展类库 (ext/)                 │  │
│  │  └─────────────────────────────────┘  │
│  └─────────────────────────────────┘    │
│  ┌─────────────────────────────────┐    │
│  │   开发工具                        │    │
│  │   javac, javadoc, jar, jdb, javap │    │
│  └─────────────────────────────────┘    │
└─────────────────────────────────────────┘
              │
              ▼
        ┌─────────────┐
        │    IDE       │  ← 集成开发环境
        │ (IntelliJ IDEA│    代码编辑器 + 构建工具 + 调试器 + ...
        │  Eclipse,     │
        │  VS Code)    │
        └─────────────┘

详细对比

组件 全称 作用 包含关系
JDK Java Development Kit 开发 + 运行 Java 程序的完整工具包 包含 JRE + 开发工具
JRE Java Runtime Environment 运行 Java 程序的环境(不含编译器) 包含 JVM + 核心类库
JVM Java Virtual Machine 执行字节码的虚拟机,内存管理、GC、线程调度 JRE 的核心
IDE Integrated Development Environment 集成开发环境,提供代码编辑、调试、构建、版本控制等 独立于 JDK,但依赖 JDK

关键结论

问题 答案
每次请求都重新编译吗? 。类只加载一次,字节码只编译一次(JIT 缓存机器码)
每次请求都重新类加载吗? 。类加载在首次使用时完成,后续复用
多次请求 JVM 在做什么? 复用已有的类、方法、对象;线程从池里取;JIT 持续优化热点代码
请求越多性能越好? 。热点方法被 JIT 编译后,执行速度接近 C++

springboot项目从启动到运行的大致流程

sql 复制代码
┌─────────────────────────────────────────────────────────────┐
│              构建阶段(Build Time)------ 编译成 .class              │
│              执行者:Maven / Gradle + javac                    │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. mvn clean package   或   ./gradlew build                │
│                                                              │
│     └─► 扫描 src/main/java 下的所有 .java 文件                 │
│                                                              │
│     └─► 调用 javac 编译                                      │
│         UserController.java ──► UserController.class         │
│         UserService.java    ──► UserService.class            │
│         ...                                                  │
│                                                              │
│     └─► 编译后的 .class 文件输出到                           │
│         target/classes/  (Maven)                             │
│         build/classes/   (Gradle)                            │
│                                                              │
│     └─► 打包成可执行 JAR                                     │
│         target/app-1.0.jar                                   │
│         结构:                                               │
│         ├─ BOOT-INF/classes/    ← 业务 .class 文件          │
│         ├─ BOOT-INF/lib/        ← 依赖 JAR(Spring、Tomcat等)│
│         └─ META-INF/MANIFEST.MF ← 主类入口                   │
│                                                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│              启动阶段(Startup)------ JVM 加载运行                 │
│              执行者:java -jar app.jar                         │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  2. java -jar app.jar                                        │
│                                                              │
│     └─► JVM 启动                                             │
│         ├─ 加载核心类库(rt.jar / modules)                   │
│         ├─ 创建 Bootstrap / Ext / App ClassLoader             │
│         └─ 找到 MANIFEST.MF 中的 Main-Class                   │
│                                                              │
│     └─► SpringApplication.run() 执行                        │
│         ├─ 创建 Spring 上下文                                 │
│         ├─ 扫描并加载 BOOT-INF/classes/ 下的 .class            │
│         ├─ 从 BOOT-INF/lib/ 加载依赖 JAR 中的 .class          │
│         ├─ 实例化 Bean、依赖注入                               │
│         └─ 启动内嵌 Tomcat(创建非守护线程)                    │
│                                                              │
│  3. 类加载器工作(此时加载的是已编译好的 .class)                │
│     └─► AppClassLoader / LaunchedURLClassLoader              │
│         从 JAR 包内读取 .class 字节码 → 加载到方法区             │
│                                                              │
│  4. JIT 编译(运行时才发生)                                   │
│     └─► 解释执行字节码 → 热点检测 → C1/C2 编译为机器码           │
│                                                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│              运行阶段(Runtime)------ 处理请求                     │
│              执行者:JVM + Spring + Tomcat                    │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  5. 请求到来 → Worker 线程从线程池取出                         │
│     └─► 执行 Controller 方法(已加载的 .class 字节码)          │
│     └─► 热点方法被 JIT 编译后直接执行机器码                     │
│                                                              │
└─────────────────────────────────────────────────────────────┘
相关推荐
程序员二叉18 小时前
【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理
java·开发语言·jvm·算法·面试·juc
小马爱打代码1 天前
面试题:内存模型与垃圾回收深度解析
jvm
cfm_29141 天前
JVM底层源码深度解析:读写屏障(Read/Write Barrier)
jvm
wuminyu1 天前
Java世界中StringTable源码剖析
java·linux·c语言·jvm·c++
醉颜凉1 天前
Elasticsearch性能优化:JVM GC调优全攻略,彻底解决集群卡顿、吞吐量下降问题
jvm·elasticsearch·性能优化
顺风尿一寸1 天前
从 Java 到内核:探秘线程改名的完整路径
jvm
lihao lihao1 天前
linux线程
java·开发语言·jvm
苏克贝塔2 天前
.NET开发之.net framework对比.net core
jvm
cfm_29142 天前
JVM垃圾收集算法与收集器深度解析
jvm·测试工具·算法·性能优化