背景
OpenJDK 发行版里默认的 Java 虚拟机实现是 HotSpot(C++)。你安装的 java 命令、容器里的 JDK 镜像,除非明确换用 OpenJ9 或 GraalVM Native Image,否则跑的就是 HotSpot。
Java 领域还有哪些 JVM,见 Java 虚拟机。
HotSpot 是什么
HotSpot 得名于 热点探测(hot spot detection):大部分时间只执行少量代码,JIT 优先编译这些热点方法。内置 解释器、C1 与 C2 两个 JIT 编译器,默认通过 分层编译 在启动速度与峰值性能之间折中。
| 对比 | HotSpot | Eclipse OpenJ9 | GraalVM Native Image |
|---|---|---|---|
| 典型形态 | OpenJDK 默认 java |
IBM Semeru | native-image 产物 |
| JIT | C1 + C2 | 自有 JIT | 构建期 AOT,无运行时 JIT |
| 常用诊断 | jcmd、JFR、Attach | 自有工具 | 与传统 JVM 排障不同 |
历史
HotSpot 的技术 lineage 来自 Self / Strongtalk 一脉的动态编译与热点探测。Longview Technologies(对外也称 Animorphic)1994 年成立,1997 年 2 月被 Sun 收购;收购后 Sun 在其 VM 技术上为 Java 编写更强的 JIT(C2 等),因运行时持续探测并优化热点代码而得名 HotSpot。
| 时间 | 节点 |
|---|---|
| 1999-04 | 作为独立产品 Java HotSpot Performance Engine 首次发布,可配合 JDK 1.2 使用 |
| 2000-05 | JDK 1.3 起 HotSpot 成为 JDK 默认 VM(Classic VM 仍可选) |
| 2002-02 | JDK 1.4 起移除 Classic VM,HotSpot 成为 唯一 JVM 实现 |
| 2006-11 | Sun 以 GPL v2 开源 HotSpot(与 OpenJDK 一并发布) |
| 2010 起 | Oracle 收购 Sun 后继续维护;OpenJDK 为参考实现 |
JDK 1.0~1.2 附带的是 Sun 自研的 Classic VM(以解释执行为主,后期部分平台有 JIT),与今天的 HotSpot 不是同一套实现。版本脉络见 Java 版本历史;各 JDK 版本要点见 JDK 1.0、JDK 1.4 等。
核心组成
Java 源码 → javac → .class
↓
类加载(见 [classloader](./classloader.md))
↓
解释执行 + C1/C2 即时编译(见 [hotspot-jit](./hotspot-jit.md))
↓
运行时数据区(堆、栈、元空间等)
↓
垃圾收集器(见 [java-gc](./gc.md))
启动与调优通过 JVM 启动参数(-X、-XX)配置,见 HotSpot JVM 启动参数。
内置诊断能力
不另挂 agent jar 也可使用的 HotSpot 能力:
- AttachListener + Diagnostic Command(jcmd、Attach API)
- JPDA / JVMTI 调试后端(JPDA、JVMTI)
- JFR(Java Flight Recorder)
动态挂外部 agent(BTrace、Arthas)同样依赖 HotSpot 的 attach 与 instrument 实现。
libjvm.so 与 JVM 主体
libjvm.so 才是 JVM 的真正主体。/usr/bin/java 只是个极小的启动器(几 KB),它的全部工作是解析参数、定位 JRE 安装路径,然后 dlopen("libjvm.so"):
java -jar app.jar
↓
java 启动器(几 KB)
→ 找到 $JAVA_HOME/lib/server/libjvm.so
→ dlopen(libjvm.so) ← JVM 主体进入内存
↓
JVM 初始化:堆、栈、方法区、GC、JIT
↓
读 MANIFEST.MF → 加载主类 → ...
libjvm.so 位于 $JAVA_HOME/lib/server/libjvm.so,通常是 JDK 安装里最大的单个文件(数十 MB)。它包含:
| 组成 | 说明 |
|---|---|
| 字节码解释器 | 执行 .class 字节码 |
| JIT 编译器 | C1(client)、C2(server) |
| GC 实现 | Serial、Parallel、G1、ZGC、Shenandoah |
| 类加载器 | Java ClassLoader 的 C++ 实现 |
| 运行时 | 线程管理、锁、内存管理 |
Spring Boot fat jar 启动时 libjvm.so 何时进内存,见 Spring Boot Executable JAR §启动全流程。
实现语言与自举
HotSpot(OpenJDK / Oracle JDK 自带的 JVM)主要用 C++ 编写,少量 C 与平台相关汇编:
| 组成 | 语言 |
|---|---|
| 解释器、GC、类加载 | C++ |
| OS 级原语(线程、内存映射) | C |
| JIT 生成的机器码模板(Stub) | 汇编(x86 / ARM) |
这是自然选择:管理裸内存、操作 CPU 寄存器、发起系统调用正是 C/C++ 擅长的。Java 无法用自己实现虚拟机——运行任何 Java 程序都已经需要一个现成的 JVM,这是「先有鸡还是先有蛋」的问题。JDK 标准库(java.lang.* 等)是 Java 写的,但任何触及 OS 的操作(文件、网络、线程)最终都会调到 libjvm.so 里的 native 方法。
Java vs Go vs Rust 的自举
「自举」(self-hosting)指一门语言的编译器用它自己写,并能编译自己。
| 语言 | 编译器 | 运行时 | 是否完全自举 |
|---|---|---|---|
| Java | javac(Java) |
JVM(C++) | 部分:javac 能编译 javac,但 JVM 是 C++,且运行 javac 本身就需要现成 JVM |
| Go | gc(Go,自 1.5 起) |
无独立 VM | 完全:编译为本地机器码 |
| Rust | rustc(Rust) |
无独立 VM | 完全:编译为本地机器码 |
Java: .java → javac(Java) → .class → JVM(C++) 执行 ← 始终依赖 C++
Go: .go → gc(Go) → 机器码 ← 完全脱离 C
Rust: .rs → rustc(Rust) → 机器码 ← 完全脱离 C
Go 与 Rust 能彻底摆脱 C 的关键,是它们直接编译为本地机器码,无需 C 写的运行时来执行编译器输出。Java 走 GraalVM Native Image 也能产出本地二进制(见 GraalVM Native Image)。
博客内相关文章
| 主题 | 文章 |
|---|---|
| JVM 生态与选型 | jvm |
| JIT / 分层编译 / C1·C2 | hotspot-jit |
| 启动参数(堆、GC、日志、JIT) | hotspot-options |
| GC 概念与算法 | java-gc |
| 对象内存布局与历史调优笔记 | jvm-memory-tuning-notes |
| 类加载 | classloader |
| Attach / jcmd | attach-api、jcmd |
| 字节码织入 | java-asm |
| 开发期热替换 | DCEVM 与 HotSwapAgent |
| Safepoint | safepoint |
| JDK 版本变更 | JDK 21→25、JDK 26 |
参考
维护记录
| 时间 | 修改内容 | 原因 |
|---|---|---|
| 2026-06-19 | 明确 HotSpot 边界;索引表增加 hotspot-options、jvm-compiler | 与 jvm.md 职责拆分 |
| 2026-06-21 | 新增「历史」节:1999 首次发布、1.3 默认、1.4 唯一 | 补充 HotSpot 起源与 JDK 集成时间线 |
| 2026-06-27 | 新增「libjvm.so 与 JVM 主体」「实现语言与自举」(含 Java/Go/Rust 自举对比) | 合并 comments-tree 启动打包文档相关章节 |
| 2026-07-15 | 更新 jvm-compiler 互链为 hotspot-jit | jvm-compiler.md 更名为 hotspot-jit.md |
| 2026-07-16 | 相关文章表中删除失效的 jvm-gc 链接,改为 jvm-memory-tuning-notes | jvm-gc.md 更名为 jvm-memory-tuning-notes.md 并精简重复内容 |