# JVM 由哪些部分组成?
Table of Contents
JVM 由哪些部分组成?
JVM 组成部分详解
【核心定义】
JVM(Java Virtual Machine,Java虚拟机)是一个为执行Java字节码而设计的、与具体操作系统和硬件平台无关的虚拟计算机系统,它通过类加载、内存管理、即时编译、垃圾回收等核心机制,实现了Java“一次编写,到处运行”的核心承诺。
【关键要点】
-
类加载子系统(Class Loader Subsystem)
- 职责:负责查找、加载、链接(验证、准备、解析)和初始化类文件(.class)。
- 双亲委派模型:加载请求首先委派给父加载器,避免核心类被篡改,保证安全与唯一性。
-
运行时数据区(Runtime Data Areas)
- 程序计数器(PC Register):线程私有,指向当前线程正在执行的字节码指令地址。
- Java虚拟机栈(JVM Stack):线程私有,存储栈帧(局部变量表、操作数栈、动态链接、方法出口)。
- 本地方法栈(Native Method Stack):为Native方法服务。
- Java堆(Java Heap):线程共享,存放所有对象实例和数组,是GC管理的主要区域。
- 方法区(Method Area):线程共享,存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等。JDK 8后由元空间(Metaspace) 实现。
-
执行引擎(Execution Engine)
- 解释器(Interpreter):逐行解释执行字节码。
- 即时编译器(JIT Compiler):将热点代码(HotSpot)编译成本地机器码,大幅提升执行效率。
- 垃圾回收器(Garbage Collector):自动回收堆中不再使用的对象,是JVM内存管理的核心。
-
本地方法接口(JNI, Java Native Interface)
- 提供调用本地(C/C++)库函数的能力,是Java与底层系统交互的桥梁。
-
本地方法库(Native Method Library)
- 由JNI调用的、用其他语言编写的库集合。
【深度推导/细节】
核心矛盾:性能、内存与跨平台性的平衡 JVM的设计是一个精妙的权衡:
- 跨平台性:通过定义统一的字节码规范和运行时数据区,屏蔽底层差异,由各平台的JVM实现负责适配。
- 性能瓶颈:纯解释执行效率低下。JVM引入分层编译(Tiered Compilation) 和热点探测机制解决。
- 解释器:启动快,用于冷代码。
- C1编译器(客户端编译器):编译快,进行简单优化。
- C2编译器(服务端编译器):编译慢,进行激进优化,生成高质量本地代码。
- 内存管理:堆内存的自动回收是核心挑战。不同垃圾回收器(如Serial, Parallel, CMS, G1, ZGC)在吞吐量、停顿时间、内存开销上做出不同取舍,以适应不同应用场景。
【关联/对比】
- JVM vs JRE vs JDK:
- JVM:Java程序运行的核心。
- JRE = JVM + 核心类库,是运行环境。
- JDK = JRE + 开发工具(如javac, javadoc),是开发套件。
- 不同JVM实现:
- HotSpot (Oracle):最主流,强调自适应优化。
- OpenJ9 (IBM/Eclipse):低内存占用和快速启动。
- GraalVM:支持多语言,可将Java程序编译为本地可执行文件。
『面试官追问』
-
程序计数器为什么是线程私有的?
因为CPU时间片轮转,线程切换后需要知道从哪里继续执行。私有保证各线程互不干扰。
-
Java虚拟机栈可能会抛出什么错误?
StackOverflowError:线程请求栈深度超过虚拟机允许深度(如无限递归)。OutOfMemoryError:栈扩展时无法申请到足够内存。
-
元空间(Metaspace)与永久代(PermGen)的区别?
永久代(JDK 7及之前):是堆的一部分,大小固定,易引发
java.lang.OutOfMemoryError: PermGen space。 元空间(JDK 8及之后):使用本地内存,默认无上限(受物理内存限制),类元数据生命周期与类加载器绑定,GC效率更高。 -
一个对象从创建到回收,在JVM里经历了什么?
Step 1: 类加载检查。Step 2: 在堆中分配内存(指针碰撞或空闲列表)。Step 3: 内存空间初始化为零值。Step 4: 设置对象头(哈希码、GC分代年龄、锁状态等)。Step 5: 执行
<init>方法初始化。Step 6: 对象被引用使用。Step 7: 不可达时,被垃圾回收器标记并回收。 -
JIT编译器如何确定“热点代码”?
基于方法调用计数器和回边计数器。超过阈值(
-XX:CompileThreshold,客户端模式默认1500,服务端模式默认10000)即触发编译。
【直击痛点:关键数字与设计】
- -Xmx和-Xms:最大堆内存和初始堆内存。合理设置可减少GC次数和动态扩容开销。
- 年轻代与老年代比例(-XX:NewRatio ):默认2,即老年代:年轻代 =2:1 。适应“对象朝生夕死”的假设。
- Survivor区比例(-XX:SurvivorRatio ):默认8,即Eden:Survivor =8:1 。大部分对象在Eden区创建。
- 大对象直接进入老年代的阈值(-XX:PretenureSizeThreshold ):避免大对象在年轻代反复复制。
【版本差异】
- JDK 7:将字符串常量池从永久代移到了堆中。
- JDK 8:彻底移除永久代,引入元空间;引入新的垃圾回收器G1作为实验特性。
- JDK 11:G1成为默认GC;引入低延迟的ZGC(实验特性)。
- JDK 17:ZGC和Shenandoah GC不再标记为实验特性,成为生产可用特性。