JVM全称是Java Virtual Machine,中文翻译为Java虚拟机。简单说,JVM就是Java程序运行的容器,是所有Java代码能够跨平台执行的根本原因。
为什么叫Java虚拟机?名称里的三个词各代表什么
JVM这个名字不是随便起的,拆开看就很好理解了。
Java明确了它的服务对象,这套虚拟机专为Java语言设计,随着生态发展,Scala、Kotlin、Groovy这些JVM语言也能在它上面运行,但它的底层规范和核心设计始终源于Java。
Virtual是关键中的关键,它不是一个真实的物理机器,而是一套用软件模拟出来的计算机,它有自己的指令集、寄存器、内存结构和方法调用栈,所有行为都被抽象成一套规范,行业共识认为,当年Sun公司设计JVM的初衷,就是把软件和硬件解耦,让程序不再依赖具体CPU和操作系统。
Machine强调它是一个完整的执行引擎,它有“算力”,能加载字节码、解析指令、执行运算,和物理机一样具备完整的计算能力。
所以JVM全称的含义就是:用软件模拟出来的一台专门运行Java字节码的计算机,理解了这一点,后面所有关于JVM的知识都会变得清晰。
JVM全称之外:它在Java技术栈里的位置
很多初学者会把JDK、JRE、JVM混在一起,这里一次性理清楚三者的包含关系。
- JDK(Java Development Kit):Java开发工具包,是面向开发者的完整工具链,包含编译器javac、调试工具、各种类库。
- JRE(Java Runtime Environment):Java运行时环境,是JVM的宿主,包含JVM和运行所需的类库。
- JVM:Java虚拟机,是整个体系中最底层的执行引擎,负责真正运行程序。
用一个比喻来理解:JDK是厨房的全部设施,JRE是灶台和锅具,JVM是那个真正把菜做熟的厨师。
平时开发时你装的是JDK,但部署上线时往往只需要JRE就够了,如果你不喜欢做饭,只想吃饭,那连厨房设备都不用装,坐等上菜就行这就是JVM的隔离作用。
Java虚拟机到底在干什么?从一个.java文件说起
你写了一个Hello.java文件,从保存到运行,JVM参与了全程,你可以用编辑器创建一个.java文件,然后在命令行里手动走一遍完整流程,这比任何理论讲解都直观。
第一步:编译成字节码
打开终端,运行命令:
javac Hello.java
这一步生成的.class文件里装的是字节码,不是机器码,字节码是JVM能识别的指令集合,只有JVM能读懂它,操作系统和CPU看不懂。
第二步:类加载
运行命令:
java Hello
JVM启动后,类加载子系统会把.class文件读进内存,加载、验证、准备、解析、初始化,五个阶段一步都不能少,验证阶段特别重要,它要确保字节码是合法有效的,不能有恶意指令混进来。
第三步:执行引擎干活
字节码到了执行引擎手中,有两种常见的执行方式:
- 解释执行:逐条把字节码翻译成机器码执行,启动快但整体慢。
- JIT编译:HotSpot虚拟机内置的即时编译器会把热点代码直接编译成机器码缓存起来,运行时间越长,执行速度越快,这也是为什么JVM性能在长期运行后往往比刚启动时更好。
第四步:内存回收
Java开发不用手动释放内存,垃圾回收器(GC)会自动清理不再使用的对象,这也是JVM给开发者带来的最大便利之一。
JVM最核心的价值:一次编写,到处运行
JVM存在的最大理由就是跨平台,Windows上的JVM看懂了字节码,Linux上的JVM也能看懂同一份字节码,你的Java程序是给JVM看的,不是直接给操作系统看的,所以只要目标平台装了对应的JVM,同一份.class文件就能直接跑。
与之形成鲜明对比的是C语言,C代码在不同平台上需要重新编译,因为编译产物直接面向CPU架构和操作系统,Java把这一层隔离交给了JVM去适配,程序员就省心了。
字节码的跨平台能力建立在JVM规范统一的基础上,任何厂商实现JVM,都必须遵循Sun公司制定的规范文档,不能随意更改指令行为,这也是Java生态能够保持兼容性的底层保障。
JVM和JRE的区别:一次理清两者的边界
JVM是JRE的一部分,JRE包含了JVM,但JRE除了JVM之外,还提供了一整套Java类库,比如集合框架、IO工具、网络API、数学函数,JVM负责执行指令,类库负责提供现成的功能实现。
如果你自己写了一套JVM,但是不附带Java类库,那它能运行标准Java程序吗?不能,因为程序里用到的System.out、String、ArrayList这些类都是靠Java类库提供的,JVM和类库相辅相成,谁也离不开谁。
用一句通俗的话概括:JVM是发动机,JRE是发动机加全套配件,JDK是发动机加配件加修理工具。
JVM内存怎么划分?一张图看懂运行时数据区
Java虚拟机的内存管理是面试重点,也是日常开发调优的核心,按JVM规范,运行时内存分为以下几个区域:
| 内存区域 | 线程共享 | 主要作用 | 异常类型 |
|---|---|---|---|
| 堆 | 共享 | 存放所有对象实例 | OutOfMemoryError |
| 方法区 | 共享 | 存储类元数据、静态变量、常量 | OutOfMemoryError |
| 虚拟机栈 | 私有 | 每个线程一个栈,存局部变量 | StackOverflowError |
| 本地方法栈 | 私有 | 服务native方法 | StackOverflowError |
| 程序计数器 | 私有 | 记录当前线程执行指令地址 | 无 |
其中堆是Java程序最活跃的舞台,所有new出来的对象都住在这里,堆又细分为新生代和老年代,新生代里再分为Eden区和两个Survivor区,大部分对象在Eden区出生,经过几次垃圾回收幸存下来的活对象,会被晋升到老年代。
方法区在JDK 8之后换了个实现,叫做元空间(Metaspace),元空间使用的不是JVM堆内存,而是本地内存(直接使用操作系统内存),它存放类的基本信息,比如类名、方法描述、字段描述。
虚拟机栈是每个线程私有的,创建一个线程就会分配一个栈,栈里装的是一个个栈帧,每个方法调用对应一个栈帧,方法递归调用太深,栈帧堆满,就会抛出StackOverflowError,这是在做无限递归时最常见的报错。
JVM日常应用:什么时候你会真正接触到它
虽然JVM对很多开发来说比较抽象,但它渗透在日常开发和运维的方方面面。
排查线上OOM
当系统报OutOfMemoryError时,你需要查看堆内存的使用情况,可以用JDK自带工具jmap导出堆转储文件,再用MAT或VisualVM分析大对象,也可以加上启动参数,让JVM在OOM时自动导出堆快照:
java -Xmx2g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/app.hprof -jar app.jar
- -Xmx2g:设置最大堆内存为2GB
- -XX:+HeapDumpOnOutOfMemoryError:OOM时自动导出堆快照
- -XX:HeapDumpPath:指定快照存放路径
Java虚拟机调优
JVM调优不是盲目改参数,而是基于运行数据做决策,常见步骤是先采集GC日志:
java -Xlog:gc:/data/logs/gc.log:time,uptime,level,tags -jar app.jar
然后分析GC频率和停顿时间,如果Minor GC频繁,说明新生代空间偏小;如果Full GC频繁且老年代回收后空间仍然不足,说明有大对象或内存泄漏。
调优的目标通常围绕几个方向:降低Full GC频率、减少停顿时间、合理分配堆内存比例,没有一套放之四海而皆准的参数,要根据实际业务和运行数据来定。
手工触发垃圾回收
生产环境不建议手动调用System.gc(),这个方法只是建议JVM执行GC,是否执行由JVM决定,在请求处理链路上调用它,会带来不可预测的停顿,属于副作用很大的操作,反例经常出现在新手代码里:在循环里调用System.gc(),结果程序频繁停顿,吞吐量大跌。
更换垃圾回收器
不同GC器适合不同规模的应用,JDK 8默认ParallelGC,适合吞吐量优先的场景,JDK 11及以后引入的ZGC目标是把停顿时间控制在毫秒级,适合大堆低延迟场景,可以通过启动参数选择GC器:
java -XX:+UseG1GC -jar app.jar java -XX:+UseZGC -jar app.jar
JVM性能调优的核心逻辑是什么
JVM调优并没有玄学,本质上是在做资源平衡,堆内存太小,GC频繁,吞吐量下降;堆内存太大,GC停顿时间长,响应延迟高,JVM在整个生命周期里的表现,会直接决定应用的服务质量。
业内专家指出,大多数Java应用的调优问题,根源不在JVM参数,而在代码本身,比如创建了过多的大对象、不合理的长生命周期对象引用、无意识的集合扩容,这些都会让GC压力陡增,先解决代码问题,再考虑调整JVM参数,这是正确的排查顺序。
常见问题答疑
JVM全称是什么?JVM指什么?
JVM全称是Java Virtual Machine,中文即Java虚拟机,它是一个抽象的计算机,负责执行Java字节码,实现了Java语言的跨平台特性,JVM规范由Oracle维护,任何厂商都可以基于规范实现自己的JVM版本,比如HotSpot、OpenJ9等。
JVM被问得最多的问题是什么?
HashMap原理、ConcurrentHashMap的线程安全机制、JVM内存结构、垃圾回收算法、类加载过程、ThreadLocal的隐式泄漏问题,这些问题的共同特点是把数据结构、并发、内存管理三个方向串在了一起,JVM考的不只是背概念,更重要的是理解每个设计决策背后的权衡。
如何验证本机Java环境使用的JVM版本?
在终端输入以下命令:
java -version
输出中会明确显示JVM的版本以及是哪个发行版,例如输出包含HotSpot,说明使用的是Oracle HotSpot虚拟机,使用以下命令可以查看更多JVM细节参数:
java -XX:+PrintCommandLineFlags -version
这个命令会显示JVM启动时的默认参数,能直接看到你当前版本的GC器选型和默认堆内存设置。
JVM是Java技术的底座,也是区分Java程序员水平的分水岭,掌握JVM全称只是第一步,真正理解它如何管理内存、如何执行字节码,才能在性能问题和线上故障面前游刃有余,从运行一个最简单的Hello World开始,逐步观察JVM的行为轨迹,这些知识会成为你技能树上最扎实的一环。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/614487.html





