Java虚拟机
JVM与Java体系结构
Java不仅仅是一个简单的编程语言,它是由一系列的软件与规范组成的技术体系。而JVM是Java程序的运行核心,通过字节码解释执行实现"一次编写,到处运行"特性,而Java体系结构由编程语言规范、类库体系及JVM运行时环境共同构成,支撑跨平台面向对象开发。可以说JVM是整个Java平台的基石,是将Java技术隔绝操作系统与硬件的关键部分。
为什么要学习JVM
功利点说,现在找工作的门槛越来越高,JVM知识点经常出现在面试题目中,要想通过面试,我们也需要了解JVM。但是除去面试,程序开发中我们也会遇见:
- 定位 OOM(内存溢出):通过分析堆转储(Heap Dump)和 GC 日志,快速定位内存泄漏的根源。
- 线程死锁:利用 JVM 工具(如 jstack)分析线程状态,解决多线程并发问题。
- 性能瓶颈:通过 Profiling 工具(如 VisualVM、Arthas)监控 CPU、内存使用情况,找到性能热点。
这些都需要我们具备JVM的基础知识,才能在生产中遇到类似问题迎刃而解。掌握了基础知识,我们可以更好地做到下面几点:
- 内存管理:理解 JVM 的内存结构(堆、栈、方法区等)能帮助你优化内存分配,避免内存泄漏和频繁的垃圾回收(GC)。
- 垃圾回收机制:不同场景需要不同的 GC 算法(如 G1、ZGC、Shenandoah),学习 JVM 可以合理选择并配置 GC,减少程序停顿时间。
- 代码优化:通过 JIT 编译器、逃逸分析等机制,理解 JVM 如何优化代码执行,从而编写更高效的代码
Java与JVM简介
Java 自 1995 年由 Sun Microsystems 发布以来,凭借其 跨平台能力、面向对象特性 和 丰富的生态系统,迅速成为全球最流行的编程语言之一。而 Java 虚拟机(JVM)作为 Java 技术的核心引擎,通过“一次编写,到处运行”(Write Once, Run Anywhere)的理念,彻底改变了软件开发的模式。
下面这张图是Java技术体系,从这张图我们可以了解Java与Jvm的关系。应该从这张图,我们可以形象地认识到JVM是Java语言的核心基石,所以我们要想学习好Java语言,一定要了解JVM。
Java 语言的核心特性
- 跨平台能力
Java 的跨平台性依赖于 JVM 的中间层设计。开发者编写的 .java 文件会被编译为与平台无关的 字节码(.class 文件),由 JVM 在目标平台上解释或编译执行。
示例:同一份 Java 程序可在 Windows、Linux、macOS 上运行,无需修改源码。
意义:降低多环境适配成本,推动企业级应用的快速部署。
面向对象设计(OOP)
Java 是纯粹的面向对象语言,其核心思想通过 封装、继承、多态 实现:封装:通过 private、protected 关键字隐藏实现细节,暴露安全接口。
继承:extends 实现代码复用,implements 支持多接口扩展。
多态:同一方法在不同子类中呈现不同行为(如 Animal 类的 sound() 方法被 Dog 和 Cat 重写)。
健壮性与安全性
异常处理:强制检查异常(Checked Exceptions)要求开发者显式处理潜在错误(如 IOException)。
内存管理:JVM 自动垃圾回收机制(GC)减少内存泄漏风险。
安全沙箱:通过字节码验证和安全管理器(SecurityManager)限制恶意代码访问系统资源。
现代语言特性演进
Java 持续吸收现代编程范式的优点:Java 8:引入 Lambda 表达式、Stream API,支持函数式编程。
Java 11:var 关键字简化局部变量声明,HTTP Client 支持异步请求。
Java 17:密封类(sealed class)限制继承关系,模式匹配增强代码可读性。
JVM:Java 生态的基石
- JVM 的核心作用
JVM 是 Java 程序运行的虚拟化环境,主要职责包括:
字节码解释与执行:将 .class 文件转换为机器指令。
内存管理:分配堆、栈、方法区等内存空间,并自动回收垃圾对象。
线程调度:管理多线程的创建、同步与资源竞争。
- JVM 的运行时数据区
JVM 内存划分为多个核心区域:
堆(Heap):存储对象实例,是垃圾回收的主战场。
方法区(Metaspace):存放类元数据(Java 8 后替代永久代)。
虚拟机栈:存储方法调用的栈帧(局部变量、操作数栈)。
程序计数器:记录当前线程执行的字节码位置。
本地方法栈:支持 Native 方法(如 C/C++ 库调用)。
- 类加载机制
JVM 通过 双亲委派模型 加载类:
加载:从文件、网络等来源读取 .class 文件。
验证:确保字节码符合 JVM 规范,防止恶意代码注入。
准备:为静态变量分配内存并初始化默认值。
解析:将符号引用转换为直接引用。
初始化:执行静态代码块(static{})和赋值操作。
示例:自定义类加载器可实现热部署(如 Tomcat 为每个 Web 应用单独加载类)。
- 垃圾回收(GC)机制
JVM 通过 GC 自动回收无用的对象,关键算法包括:
标记-清除:简单但易产生内存碎片。
复制算法:将存活对象复制到新空间(适用于年轻代)。
标记-整理:整理内存碎片(适用于老年代)。
分代收集:根据对象生命周期划分年轻代(Young Generation)和老年代(Old Generation)。
现代 GC 器:G1(低延迟)、ZGC(TB 级堆内存)、Shenandoah(并发回收)。
调优场景:高并发服务可通过 -XX:+UseG1GC 启用 G1 垃圾回收器,减少停顿时间。
- JIT 编译器
JVM 通过 即时编译(Just-In-Time Compilation) 提升性能:
解释执行:初期逐行解释字节码,启动速度快。
热点代码优化:频繁执行的代码(热点代码)被编译为本地机器码。
逃逸分析:优化对象分配(如栈上分配、锁消除)。
JVM的架构模型
Java编译器输入的指令流是一种基于栈的指令集架构,另外一种常见的指令集架构则是基于寄存器的指令集架构。计算机指令集架构(ISA)是硬件与软件交互的核心接口,决定了程序如何被编译和执行。基于栈的指令集架构(如 JVM 字节码)和 基于寄存器的指令集架构(如 x86、ARM)是两种经典的设计范式,它们在指令执行方式、性能特点和应用场景上存在显著差异。
基于栈的指令集架构(Stack-Based)
核心原理
数据操作依赖栈结构:所有计算通过操作数栈(Operand Stack)完成。指令从栈顶取操作数:例如,加法指令 iadd 会弹出栈顶两个整数,相加后结果压回栈顶。无需显式指定操作数地址:指令本身不包含寄存器或内存地址,隐含依赖栈顶数据。典型示例(JVM 字节码)
java
// Java 代码:计算 3 + 5
int a = 3;
int b = 5;
int c = a + b;
对应的字节码:
字节码
iconst_3 // 将常量3压入栈顶
istore_1 // 弹出栈顶值(3),存入局部变量表第1槽位(a)
iconst_5 // 将常量5压入栈顶
istore_2 // 弹出栈顶值(5),存入局部变量表第2槽位(b)
iload_1 // 加载局部变量a的值(3)到栈顶
iload_2 // 加载局部变量b的值(5)到栈顶
iadd // 弹出栈顶两个值(3和5),相加后结果(8)压入栈顶
istore_3 // 弹出栈顶值(8),存入局部变量表第3槽位(c)
- 优点
指令紧凑:无需指定操作数地址,指令长度短(如 JVM 字节码通常为 1-2 字节)。
跨平台友好:不依赖物理寄存器数量或布局,适合虚拟机实现(如 JVM)。
代码生成简单:编译器无需处理寄存器分配,逻辑更简单。
- 缺点
执行速度较慢:频繁的入栈、出栈操作导致内存访问开销大。
指令数量多:简单操作可能需要多条指令(如加载变量到栈顶再计算)。
基于寄存器的指令集架构(Register-Based)
- 核心原理
数据操作依赖寄存器:指令直接读写寄存器中的操作数。
显式指定操作数地址:指令需声明操作数所在的寄存器或内存地址。
结果直接写入寄存器:例如,加法指令 ADD R1, R2, R3 表示 R1 = R2 + R3。
2. 典型示例(ARM 汇编)
// 计算 3 + 5,结果存入寄存器 R0
MOV R1, #3 // 将立即数3存入寄存器R1
MOV R2, #5 // 将立即数5存入寄存器R2
ADD R0, R1, R2 // R0 = R1 + R2
- 优点
执行效率高:减少内存访问次数,数据直接在寄存器中操作。
指令数量少:单条指令可完成复杂操作(如 ADD 直接操作三个寄存器)。
硬件优化潜力大:与现代 CPU 的多级流水线、乱序执行等特性契合。
- 缺点
指令长度较长:需编码寄存器地址,指令占用空间更大。
依赖硬件寄存器数量:寄存器数量有限的架构(如 x86)可能需频繁内存交互。
编译器复杂度高:需优化寄存器分配策略(如避免寄存器溢出)。
JVM生命周期
JVM的生命周期分为三个状态:启动、执行和推出
- 启动: JVM可以通过java命令启动,接着通过引导类加载器加载类文件,最后找到程序中的main方法,接着开始执行Java应用程序
- 执行: JVM的执行,表示一个已经启动的JVM开始执行Java程序,执行一个Java程序,真正执行的是一个JVM的进程。
- 退出: JVM的退出有下面几种情况
- Java程序正常结束,所有的非守护线程结束
- Java程序异常
- 操作系统故障
- 用户手动关闭JVM
- 调用Runtime或者System的exit方法
总结
我们本章简单介绍一下Java语言与JVM的结构,之后我们会在专栏文章中,逐步把我们上面介绍的知识点都覆盖到,争取能够做到让大家能够对JVM的知识有一个宏观的认识。