Skip to content

HotSpot VM(核心)

JVM 世界的霸主

如果说 JVM 规范是一本宪法,那 HotSpot 就是这部宪法的最高法院。Oracle JDK、OpenJDK、AdoptOpenJDK、Amazon Corretto、Azul Zulu——几乎所有你用到的 JDK,内置的 JVM 都是 HotSpot,或者基于 HotSpot。

1999 年 HotSpot 被 Sun 收购并发布,2006 年 Sun 将 Java 开源(OpenJDK 项目),HotSpot 也随之开源。时至今日,HotSpot 是历史上使用最广泛的 Java 虚拟机。

为什么叫 HotSpot

HotSpot 的核心创新是热点探测(Hotspot Detection)

程序员写代码时,有些代码被执行一次就再也不用了(如类的初始化代码),有些代码被反复执行(如循环内的计算、常用的方法)。HotSpot 会监控代码的执行频率,找出那些「热点」代码,然后对它们进行激进优化。

java
public class HotspotDemo {
    public static void main(String[] args) {
        // 这段代码会执行一次,不值得 JIT 优化
        System.out.println("Startup...");

        // 但这个循环会执行 N 次(假设 N = 1,000,000)
        // HotSpot 会把它识别为热点,JIT 编译成本地机器码
        long sum = 0;
        for (int i = 0; i < 1_000_000; i++) {
            sum += i;  // 被执行 100 万次,JIT 会重点优化这里
        }
        System.out.println(sum);
    }
}

HotSpot 的架构

HotSpot 由三个主要组件构成:

┌─────────────────────────────────────────────────────────────┐
│                      HotSpot VM                            │
│                                                             │
│  ┌──────────────────┐  ┌────────────────────────────────┐ │
│  │   VM Runtime     │  │        Opto Runtime(优化运行时) │ │
│  │  - 解释器        │  │  - C1/C2 编译器                 │ │
│  │  - 调优参数管理   │  │  - 寄存器分配                    │ │
│  │  - 异常处理       │  │  - 激进优化                      │ │
│  │  - 内存管理       │  │  - 去优化(Deoptimization)       │ │
│  └──────────────────┘  └────────────────────────────────┘ │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                   Memory Manager                       │  │
│  │   堆(Heap)│ 方法区(元空间)│ 直接内存│ GC子系统    │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                Interpreter / Compilers                 │  │
│  │     解释器(启动)     C1 编译器      C2 编译器        │  │
│  │     (字节码执行)   (快速编译)    (深度优化)           │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

热点探测与分层编译

HotSpot 使用计数器和轮廓(Profile) 来识别热点代码。

热点探测的两个计数器

  • 方法调用计数器:统计一个方法被调用的次数。超过阈值(默认 10000 次),触发 JIT 编译
  • 回边计数器:统计循环体被执行多少次。超过阈值(默认 14000 次),触发 JIT 编译

分层编译(Tiered Compilation)

HotSpot 有两种 JIT 编译器:

编译器别名特点适用场景
C1(Client Compiler)-client 模式编译速度快,优化较少桌面应用、短时运行程序
C2(Server Compiler)-server 模式编译速度慢,优化激进服务器、长时运行程序

分层编译让两者协同工作:

Level 0:解释执行(启动快)
    ↓ 收集 Profile 数据
Level 1:C1 快速编译(编译快,优化少)
    ↓ 收集更详细的 Profile
Level 2:C1 + 激进优化(编译较快,有一定优化)
    ↓ 收集完整 Profile
Level 3:C2 深度编译(编译慢,最深优化)

实战建议:JDK 8+ 默认就是分层编译,不需要手动指定 -client-server。如果用 JDK 7 之前的版本,需要根据场景选择。

HotSpot 的 GC 演进

HotSpot 的 GC 发展史,就是 JVM 性能优化的历史:

Serial GC(串行)  ──→  Parallel GC(并行)  ──→  CMS  ──→  G1  ──→  ZGC/Shenandoah
1999               2002              2006       2012    2019+
单线程             多线程并行         并发标记    区域化   亚毫秒停顿
停顿时间长         吞吐量优先         停顿可控    平衡型   极致低停顿
GC特点适用场景
Serial GC单线程,Stop-The-World单核、小内存、Client 模式
Parallel GC多线程并行,吞吐量优先多核、大内存、批处理
CMS GC并发标记,停顿短低延迟服务(已被废弃)
G1 GC区域化,平衡吞吐和停顿JDK 9+ 默认,大多数场景
ZGC / Shenandoah亚毫秒停顿,扩展性好大内存、低延迟场景

关于每种垃圾回收器的详细内容,我们会在后续 GC 专题深入讲解。

HotSpot 的核心参数

HotSpot 提供了大量的启动参数来控制其行为。以下是几个最常用的参数:

堆内存设置

bash
# 初始堆大小
java -Xms256m MyApp

# 最大堆大小
java -Xmx4g MyApp

# 设置年轻代大小(JDK 8 及之前)
java -Xmn2g MyApp

# JDK 17+,推荐用比例设置
java -XX:NewRatio=2 -XX:MaxMetaspaceSize=512m MyApp

GC 选择

bash
# 使用 G1 GC(JDK 11+ 默认,推荐)
java -XX:+UseG1GC MyApp

# 使用 ZGC(低延迟场景)
java -XX:+UseZGC MyApp

# 使用 Serial GC(单核/小内存)
java -XX:+UseSerialGC MyApp

JIT 相关

bash
# 打印 JIT 编译日志
java -XX:+PrintCompilation MyApp

# 打印 GC 详情
java -XX:+PrintGCDetails -XX:+PrintGCDateStamps MyApp

热点阈值调整

bash
# 方法调用次数阈值(默认 10000)
java -XX:CompileThreshold=15000 MyApp

# 打印编译后的方法
java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining MyApp

HotSpot 的去优化机制

JIT 编译器做的优化都是基于「假设」的。如果运行时假设被打破(比如加载了新类、子类继承覆盖了方法),HotSpot 会去优化(Deoptimization)——撤销之前编译的代码,回到解释执行。

java
public class DeoptimizationDemo {
    static class A {
        int method() { return 1; }  // 编译后假设只有 A.method() 被调用
    }

    static class B extends A {
        int method() { return 2; }  // 加载 B 后,A 的 JIT 优化被撤销
    }

    public static void main(String[] args) {
        A a = new A();
        for (int i = 0; i < 20000; i++) {
            a.method();  // 热点,触发 JIT
        }

        // 突然加载了 B 类
        a = new B();
        for (int i = 0; i < 20000; i++) {
            a.method();  // 之前的 JIT 假设不成立了,去优化!
        }
    }
}

去优化是 HotSpot「保守但安全」策略的体现:它会在运行时持续验证自己的假设,一旦不对就回退,绝不会为了性能而牺牲正确性。

GraalVM 与 HotSpot 的关系

从 JDK 9 开始,HotSpot 引入了一个重要的变化:把 JIT 编译器从 C++ 实现的 HotSpot 内部,抽取成可插拔的组件

这意味着你可以把 HotSpot 的默认 C2 编译器替换成 GraalVM(一个用 Java 实现的 JIT 编译器):

传统 HotSpot:
  字节码 → [HotSpot 内置 C2 编译器] → 本地机器码

GraalVM:
  字节码 → [Graal JIT 编译器] → 本地机器码

  字节码 → [Graal AOT 编译器] → 原生可执行文件

GraalVM 是 HotSpot 的「超集」——它既可以作为 HotSpot 的 JIT 插件使用,也可以作为独立虚拟机运行。它代表了 JVM 技术的未来方向。

本节小结

HotSpot 是目前使用最广泛的 JVM,它的核心设计哲学是:

  • 热点驱动:只对热点代码做激进优化
  • 分层编译:平衡启动速度和运行效率
  • 安全保守:假设被打破时主动去优化
  • GC 多样:提供多种回收器应对不同场景

理解 HotSpot 的内部机制,是深入理解 JVM 的必经之路。本教程后续的每一个章节,几乎都是在 HotSpot 基础上展开的。

下一节,我们来看看 JRockit/IBM J9 VM,了解另外两款在企业级领域有独特建树的 JVM。

基于 VitePress 构建