Java 程序运行机制
写完代码点运行,程序就跑起来了。但 javac 编译了什么?JVM 又是怎么执行代码的?理解运行机制,才能理解 Java 跨平台、GC、JIT 等核心特性的原理。
整体流程
源代码(.java)
↓ javac 编译
字节码(.class)
↓ java 运行
JVM 执行
↓
本地机器码字节码:平台无关的中间语言
Java 源代码经过 javac 编译后,生成 .class 文件,这就是字节码:
java
// 源代码
public class Hello {
public static void main(String[] args) {
System.out.println("Hello");
}
}编译后生成 Hello.class,用十六进制查看(部分):
CA FE BA BE 00 00 00 37 00 0D 0A 00 05 00 0C 07
00 0D 07 00 0E 0C 00 09 00 03 00 0C 01 00 06 3C
69 6E 69 74 3E 01 00 03 28 29 56 ...字节码是一种中间代码,不是机器码。它由 JVM 执行,而不是 CPU 直接执行。关键点:字节码是统一的,JVM 是多样的——不同平台有各自的 JVM 实现,但字节码格式完全一致。
javac 编译时做了四件事:语法检查、语义检查、符号解析、生成字节码。
JVM 架构
┌─────────────────────────────────────────────────────────────┐
│ JVM │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 类加载器 │ │ 执行引擎 │ │ 本地接口 │ │
│ │ ClassLoader │ │ Interpreter│ │ JNI/Native│ │
│ └──────┬──────┘ │ JIT │ └─────────────┘ │
│ │ └──────┬──────┘ │
│ ┌──────▼────────────────▼──────────────────────────┐ │
│ │ 运行时数据区 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 方法区 │ │ 堆 │ │ 栈 │ │ PC │ │ │
│ │ │MethodArea│ │ Heap │ │ Stack │ │Register │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘类加载器
三层结构:Bootstrap ClassLoader(核心类库)→ Platform ClassLoader(扩展类)→ Application ClassLoader(应用程序类)。双亲委派模型保证了类的安全性——你写的 java.lang.String 不会覆盖核心类库。
运行时数据区
| 区域 | 用途 |
|---|---|
| 堆(Heap) | 对象实例、数组,GC 主要管理区域 |
| 栈(Stack) | 方法调用栈帧、局部变量 |
| 方法区(Method Area) | 类信息、常量池、静态变量 |
| 程序计数器(PC) | 当前执行字节码行号 |
执行引擎
解释器逐行执行字节码,JIT 编译器将热点代码编译为本地机器码,GC 自动回收不再使用的对象。
JIT 编译
现代 JVM 采用分层编译,平衡启动速度和运行效率:
| 层次 | 说明 | 特点 |
|---|---|---|
| Tier 0 | 解释执行 | 启动快 |
| Tier 1 | C1 编译 | 快速编译,优化一般 |
| Tier 2 | C2 编译 | 深度优化,编译较慢 |
JVM 通过热点检测识别热点代码:方法调用计数器统计方法调用次数,回边计数器统计循环体执行次数。超过阈值(如 10000 次)的代码会被 JIT 编译为机器码。
这带来一个有趣的现象:Java 程序运行得越久,往往越快。
为什么 Java 能跨平台
三个关键点:字节码统一、JVM 适配各平台、接口一致。
同一份 Hello.class
│
├── Windows JVM → Windows 机器码
├── Linux JVM → Linux 机器码
└── macOS JVM → macOS 机器码后续可阅读:JVM/JRE/JDK 区别、编译型 vs 解释型、垃圾回收原理
