Java学习第六十部分——JVM
目录
一. 关键概述
二. 核心功能
三. 组成架构
1. 类加载子系统 (ClassLoader)
2. 运行时数据区 (Runtime Data Areas)
3. 执行引擎 (Execution Engine)
四. 类加载机制
五. 垃圾回收 (GC)
六. JIT 编译器 (Just-In-Time)
七. 调优参数示例
八. 常见问题与工具
九. 与 JDK/JRE 的关系
十. 调优与监控
十一. 常见实现
十二. 简单示例
十三. 总结归纳
一. 关键概述
Java 虚拟机(JVM,Java Virtual Machine)是 Java 平台的核心组成部分,负责将编译后的字节码(`.class` 文件)解释或编译为机器码,并在不同操作系统上实现“一次编写,到处运行”(Write Once, Run Anywhere)的跨平台特性。
二. 核心功能
-
跨平台执行:将字节码翻译成不同操作系统的本地机器指令。
-
内存管理:自动分配/回收内存(堆、栈等),避免手动管理错误。
-
垃圾回收 (GC):自动回收无用的对象内存。
-
字节码执行:通过解释器或 JIT 编译器执行字节码。
三. 组成架构
1. 类加载子系统 (ClassLoader)
组件 | 功能描述 | 关键特性 |
---|---|---|
Bootstrap ClassLoader | 加载 JAVA_HOME/lib 的核心类库(如 rt.jar ) | 由 C++ 实现,JVM 自身的一部分 |
Extension ClassLoader | 加载 JAVA_HOME/lib/ext 目录的扩展类库 | 继承自 URLClassLoader ,父加载器为 Bootstrap |
Application ClassLoader | 加载用户类路径(classpath)的类 | 默认线程上下文类加载器,父加载器为 Extension |
双亲委派模型 | 加载类时优先委派父类加载器处理 | 避免核心类被篡改(如自定义 java.lang.String ),确保安全性和类唯一性 |
2. 运行时数据区 (Runtime Data Areas)
内存区域 | 存储内容 | 特性 |
---|---|---|
方法区 (Method Area) | 类元数据(类名、字段、方法)、常量池、静态变量 | JDK 8+ 由元空间 (Metaspace) 实现,使用本地内存,替代永久代(PermGen) |
堆 (Heap) | 对象实例和数组 | 垃圾回收主区域,分为: - 新生代(Eden + Survivor From/To) - 老年代 |
虚拟机栈 (JVM Stack) | 每个线程私有,存储栈帧(局部变量表、操作数栈、动态链接、方法出口) | 方法调用压栈,返回弹栈;可能抛出 StackOverflowError |
本地方法栈 | 服务于 Native 方法(如 JNI 调用的 C/C++ 代码) | 与虚拟机栈类似,但针对本地方法 |
程序计数器 (PC Register) | 记录当前线程执行的字节码指令地址 | 唯一不会发生 OOM 的区域,线程私有 |
3. 执行引擎 (Execution Engine)
组件 | 功能描述 | 关键实现 |
---|---|---|
解释器 (Interpreter) | 逐行解释字节码为机器码执行 | 启动速度快,但运行时效率低 |
JIT 编译器 | 将热点代码(高频方法/循环)编译为本地机器码 | HotSpot 使用分层编译: - C1 编译器(客户端,快速优化) - C2 编译器(服务端,深度优化) |
垃圾回收器 (GC) | 自动回收无引用对象的内存 | 主流算法与收集器: - 新生代:复制算法(Parallel Scavenge) - 老年代:标记-清除/整理(CMS) - G1 GC(JDK9+ 默认):区域化分代 + 可预测停顿 - ZGC(JDK11+):低延迟(<10ms)、TB 级 |
核心关系总结
-
类加载 → 运行时数据区:
ClassLoader
加载的类元数据存储于方法区,对象实例分配在堆中。
-
执行引擎 → 运行时数据区:
解释器/JIT 执行栈中方法的字节码;GC 管理堆内存的回收。
-
线程私有区域:
每个线程独立拥有:虚拟机栈、本地方法栈、程序计数器。
四. 类加载机制
类加载分三个阶段:
1. **加载 (Loading)**
- 通过类加载器(`ClassLoader`)加载 `.class` 文件到内存。
- 类加载器层级:
- **Bootstrap ClassLoader**(加载核心库如 `rt.jar`)
- **Extension ClassLoader**(加载扩展库)
- **Application ClassLoader**(加载用户类路径)
- **自定义 ClassLoader**(用户扩展)。
2. **链接 (Linking)**
- 验证字节码合法性 → 准备(为静态变量分配内存)→ 解析(符号引用转直接引用)。
3. **初始化 (Initialization)**
- 执行静态代码块和静态变量赋值(如 `static int x = 10;`)。
五. 垃圾回收 (GC)
- **回收对象**:无引用指向的对象(如超出作用域的局部变量)。
- **GC 算法**:
- **分代收集**:堆分为 **新生代 (Young)** 和 **老年代 (Old)**。
- 新生代:使用 **复制算法**(Eden + Survivor 区)。
- 老年代:使用 **标记-清除** 或 **标记-整理** 算法。
- **垃圾收集器**:
- **Serial GC**:单线程,适合小应用。
- **Parallel GC**(默认):多线程,吞吐量优先。
- **CMS** / **G1 GC**:低延迟,减少 STW(Stop-The-World)时间。
- **ZGC** / **Shenandoah**(JDK 15+):超低延迟(<10ms)。
六. JIT 编译器 (Just-In-Time)
- **热点代码优化**:频繁执行的字节码会被编译成本地机器码(如循环、高频方法)。
- **解释器 vs JIT**:解释器逐行执行字节码(启动快);JIT 编译后执行(运行快)。
七. 调优参数示例
# 堆内存设置
-Xms512m # 初始堆大小(默认物理内存 1/64)
-Xmx1024m # 最大堆大小(默认物理内存 1/4)
-XX:NewRatio=2 # 老年代:新生代 = 2:1
-XX:SurvivorRatio=8 # Eden:Survivor = 8:1
# 垃圾回收器选择
-XX:+UseG1GC # 启用 G1 收集器
-XX:+UseConcMarkSweepGC # 启用 CMS 收集器
# 元空间设置(JDK 8+)
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m
八. 常见问题与工具
- **内存泄漏**:对象被意外保留引用(如静态集合类持续增长)。
- **OOM (OutOfMemoryError)**:堆/元空间不足。
- **诊断工具**:
- **jconsole** / **VisualVM**:图形化监控堆、线程、类加载。
- **jstack**:分析线程堆栈(死锁检测)。
- **jmap**:导出堆内存快照(配合 **MAT** 工具分析内存泄漏)。
- **jstat**:监控 GC 状态(`jstat -gcutil <pid> 1000`)。
九. 与 JDK/JRE 的关系
- **JDK (Java Development Kit)**:开发工具包(含 JRE + 编译器 `javac`)。
- **JRE (Java Runtime Environment)**:运行环境(含 JVM + 基础库)。
- **JVM**:执行引擎,是 JRE 的子集。
十. 调优与监控
- **常用参数**:
- 堆大小:`-Xms`(初始堆)、`-Xmx`(最大堆)。
- 新生代比例:`-XX:NewRatio`。
- GC 日志:`-Xlog:gc*`(JDK 9+)。
- 元空间:`-XX:MaxMetaspaceSize`。
- **监控工具**:
- **JVisualVM**:可视化监控线程、堆、GC。
- **JConsole**:实时查看内存、线程、类加载。
- **jstack**:查看线程堆栈(定位死锁)。
- **jmap**:生成堆转储(`jmap -dump` 分析内存泄漏)。
十一. 常见实现
- **HotSpot**(Oracle/OpenJDK 默认):支持 JIT 和多种 GC。
- **OpenJ9**(IBM):低内存占用,适合云原生。
- **GraalVM**:多语言支持(Java/Python/JS),原生镜像(AOT 编译)。
十二. 简单示例
// 内存泄漏示例:静态集合持有对象引用
public class MemoryLeak {private static final List<Object> list = new ArrayList<>();public void addData() {while (true) {list.add(new byte[1024 * 1024]); // 持续占用内存}}
}
排查步骤:
1. 使用 `jmap -dump:format=b,file=heap.hprof <pid>` 生成堆转储。
2. 通过 MAT 或 VisualVM 分析泄漏路径(如 `list` 持续增长)。
十三. 总结归纳
JVM 是 Java 生态的基石,理解其内存模型、类加载、GC 机制是优化性能的关键。掌握 JVM 调优和诊断工具,能有效解决内存溢出、高延迟等问题。JVM 通过抽象操作系统差异、自动化内存管理和高效的执行引擎,成为 Java 高性能和跨平台能力的基石。理解其原理和调优技巧,是 Java 开发者进阶的必经之路。