Skip to content

跨平台的 Java 与跨语言的 JVM

「一次编写,到处运行」的代价

1995 年,Sun 公司提出了 Java 的核心理念:Write Once, Run Anywhere(一次编写,到处运行)。这句话在当时是革命性的。

在 Java 之前,C/C++ 程序员写程序,需要针对不同平台编译不同的机器码。Windows 上的 .exe 和 Linux 上的可执行文件是不同的。程序员要在多个平台上运行程序,工作量翻倍。

Java 的解决方案是:引入一个中间层——字节码。源代码不再直接编译成机器码,而是编译成一种虚拟 CPU 能理解的指令集。这个虚拟 CPU,就是 JVM。

C/C++ 的做法:
    源代码 → [编译] → Windows 机器码
    源代码 → [编译] → Linux 机器码
    源代码 → [编译] → macOS 机器码
    每个平台都要单独编译

Java 的做法:
    源代码 → [编译] → 字节码 (.class)
    字节码 → [JVM 解释/编译] → 任意平台的机器码
    一次编译,到处运行

「代价」在于:字节码需要被 JVM 解释或编译执行,多了一层中间转换,极端情况下比原生代码稍慢。但 JIT 编译器的出现大幅弥补了这个差距。

JVM:超越 Java 的存在

JVM 的设计者可能没想到,这个最初为 Java 设计虚拟机,后来成为了多语言运行的平台

如今,跑在 JVM 上的语言远不止 Java 一门:

语言诞生年份定位
Java1995通用企业级开发
Groovy2003脚本语言,动态特性
Scala2004函数式+面向对象,大数据
Kotlin2011现代 Android/服务端开发
Clojure2007Lisp 方言,函数式
JRuby2001Ruby 实现,跑 Ruby on Rails
Ceylon2011Red Hat 推出的企业语言
Kotlin2011JetBrains 出品,Google 官方 Android 语言

有一个笑话:JVM = Java Virtual Machine,后来变成了 JVM = Many Very Magic

为什么这么多语言选择 JVM?

原因很实际:

  1. 成熟的运行时:内存管理、GC、线程调度、JIT 优化——这些脏活累活 JVM 帮你干了,语言实现者只需要专注于语法和语义
  2. 现成的生态:Java 积累了几十年的库,语言实现者可以直接拿来用
  3. 跨平台能力:编译成字节码,就天然支持所有有 JVM 的平台
  4. 高性能:JIT 编译器可以根据运行时信息做激进优化,比纯解释执行快很多

跨平台的关键:字节码规范

让多语言能跑在同一个 JVM 上,核心是遵守同一个规范:Class 文件格式

《Java 虚拟机规范》定义了 Class 文件的结构——魔数、常量池、字段表、方法表、属性表……无论什么语言,只要你的编译器能生成符合这个规范的 Class 文件,就能被 JVM 加载和执行。

这就是 JVM 的厉害之处:它不关心源代码是什么语言写的,它只关心字节码对不对

Java 编译器     →  Java 字节码
Kotlin 编译器   →  Java 字节码
Scala 编译器    →  Java 字节码
Groovy 编译器   →  Java 字节码

         统一的 Class 文件格式

         JVM 统一执行

跨平台与 JDK/JRE/JVM 的关系

很多人分不清 JDK、JRE、JVM 的区别:

组件全称包含内容用途
JVMJava Virtual Machine只包含运行时组件运行已编译的字节码
JREJava Runtime EnvironmentJVM + Java 核心类库运行 Java 程序
JDKJava Development KitJRE + javac + 调试工具开发 Java 程序

简单记:

  • 开发用 JDK(你需要编译)
  • 运行用 JRE(用户只需要跑程序)
  • JVM 是底座(JRE 和 JDK 里都有它)

平台特定与平台无关

在 Java 中,有两个维度需要注意:

平台无关的:

  • Java 源代码(.java 文件)—— 用任何文本编辑器都能写
  • 编译后的字节码(.class 文件)—— 所有平台的 JVM 都能读

平台相关的:

  • JVM 实现——每个平台有各自的实现(Windows 版 HotSpot、Linux 版 HotSpot……)
  • 本地方法(native 方法)—— 通过 JNI 调用平台特定的 C/C++ 代码
  • 直接内存—— 使用操作系统原生内存,不受 JVM 堆大小限制

理解这个区分,有助于理解为什么 Java 能跨平台,同时也有一些「平台陷阱」需要注意。

AOT 编译:另一种思路

JVM 传统的执行模式是解释+JIT 编译,即字节码在运行时被逐步翻译成本地机器码。

但从 JDK 9 开始,引入了一种新的编译模式:AOT(Ahead-Of-Time)编译

AOT 的做法是:在程序运行之前,直接把字节码编译成本地机器码。这样运行时就不需要 JIT 编译器了,启动速度更快,适合对启动时延敏感的容器化场景。

不过 AOT 也有局限性:它无法像 JIT 那样根据运行时数据进行激进优化,所以性能通常不如 JIT。因此 JIT 仍然是主流,AOT 作为补充手段。

本节小结

跨平台的 Java,核心靠的是 JVM 这个中间层:

  • Java 源代码 → javac → 字节码(平台无关)
  • 字节码 + JVM → 本地机器码(各平台执行)

JVM 的野心不止于 Java。它定义了通用的字节码规范,让任何语言只要能编译出合规的 Class 文件,就能跑在 JVM 上。这造就了今天 JVM 上多语言共存的生态。

下一节,我们来深入聊聊 字节码与多语言混合编程,看看字节码长什么样,以及为什么 Kotlin 和 Java 可以无缝互调。

基于 VitePress 构建