Java核心 之JVM

JVM(Java Virtual Machine,Java 虚拟机)是 Java 语言跨平台特性的核心,它负责将字节码(.class 文件)解释或编译为机器码并执行。以下从核心角度详解 JVM:

一、JVM 的作用与地位

  1. 跨平台基石:"一次编写,到处运行"(Write Once, Run Anywhere)的实现者,通过屏蔽不同操作系统的底层差异,使字节码在任何安装了 JVM 的平台上运行。
  2. 内存管理:自动负责内存分配与回收(垃圾回收),减少手动内存操作的风险。
  3. 执行引擎:将字节码转换为机器码(解释执行或即时编译 JIT)。

二、JVM 内存结构(基于 Java 8 及以后)

JVM 内存分为线程私有线程共享两大区域:

1. 线程私有区域(随线程创建 / 销毁)
  • **程序计数器(Program Counter Register)**记录当前线程执行的字节码指令地址(如分支、循环、跳转等),是唯一不会 OOM 的区域。

  • **虚拟机栈(VM Stack)**存储方法调用的栈帧(Stack Frame),每个栈帧包含:

    • 局部变量表(方法内变量)
    • 操作数栈(计算过程的临时数据)
    • 动态链接(指向常量池的引用)
    • 方法返回地址可能抛出StackOverflowError(栈深度溢出)、OutOfMemoryError(栈扩展失败)。
  • **本地方法栈(Native Method Stack)**类似虚拟机栈,但为 Native 方法(如 C/C++ 实现的方法)服务,HotSpot 将其与虚拟机栈合并实现。

2. 线程共享区域(随 JVM 启动 / 关闭)
  • 堆(Heap)

    • JVM 中最大的内存区域,所有对象实例及数组均在此分配。
    • 是垃圾回收(GC)的主要区域,可分为:
      • 新生代(Eden 区 + 两个 Survivor 区,比例通常 8:1:1)
      • 老年代(存放存活久的对象)可能抛出OutOfMemoryError: Java heap space
  • 方法区(Method Area)

    • 存储类信息(结构、方法、字段)、常量、静态变量、即时编译后的代码等。
    • Java 8 后由元空间(Metaspace) 实现,取代永久代,元空间使用本地内存,默认无上限(可配置)。可能抛出OutOfMemoryError: Metaspace(元空间不足)。
  • 运行时常量池(Runtime Constant Pool)

    • 方法区的一部分,存放类加载时从字节码常量池解析出的字面量(如字符串)和符号引用(如类名、方法名)。
    • 字符串常量池(String Pool)是其重要组成,JDK 7 后移至堆中。

三、类加载机制

JVM 将.class 文件加载到内存并生成可执行代码的过程,分为 5 步:

  1. 加载(Loading) 通过类全限定名(如java.lang.String)获取二进制字节流,将其转换为方法区的运行时数据结构,并在堆中生成Class对象作为访问入口。

  2. **验证(Verification)**确保字节码符合 JVM 规范(如格式正确、安全校验),防止恶意字节码攻击。

  3. 准备(Preparation) 为类的静态变量分配内存并设置初始默认值 (如int为 0,boolean为 false),不包含初始化语句(如static int a = 10中的 10 在初始化阶段赋值)。

  4. **解析(Resolution)**将常量池中的符号引用(如类名、方法名)替换为直接引用(内存地址)。

  5. 初始化(Initialization) 执行类构造器<clinit>()方法:

    • 合并静态变量赋值语句和静态代码块(按顺序执行)。
    • 父类<clinit>()先于子类执行。

四、垃圾回收(GC)

JVM 自动回收不再被引用的对象内存,核心步骤:

1. 如何判断对象可回收?
  • 引用计数法:对象被引用时计数 + 1,失效时 - 1,计数为 0 则可回收(缺点:无法解决循环引用)。
  • 可达性分析:以 "GC Roots" 为起点(如虚拟机栈引用的对象、静态变量、常量等),不可达的对象标记为可回收。
2. 垃圾回收算法
  • 标记 - 清除(Mark-Sweep):标记可回收对象后直接清除(缺点:产生内存碎片)。
  • 标记 - 复制(Mark-Copy):将存活对象复制到新区域,清除原区域(适合新生代,如 Eden 区到 Survivor 区)。
  • 标记 - 整理(Mark-Compact):标记后将存活对象向一端移动,再清除边界外内存(适合老年代)。
3. 常见垃圾收集器
  • SerialGC:单线程 GC,适用于简单应用(Client 模式)。
  • ParallelGC:多线程 GC,注重吞吐量(吞吐量 = 运行用户代码时间 / 总时间)。
  • CMS(Concurrent Mark Sweep):并发标记清除,注重低延迟(停顿时间短)。
  • G1(Garbage-First):将堆分为多个 Region,优先回收垃圾多的区域,平衡吞吐量和延迟(Java 9 后默认)。
  • ZGC/Shenandoah:超低延迟 GC(毫秒级以下),适合大内存场景。

五、执行引擎

将字节码转换为机器码的核心组件:

  1. 解释器:逐行解释字节码为机器码,启动快但执行慢。
  2. JIT 编译器(即时编译器):将热点代码(频繁执行的代码)编译为本地机器码缓存,提高执行效率(HotSpot 的 C1 编译器针对客户端,C2 针对服务端)。
  3. 混合模式:JVM 默认采用解释 + JIT 编译结合的方式,兼顾启动速度和执行效率。

六、JVM 参数调优

通过 JVM 参数配置内存大小、GC 策略等,常见参数:

  • 堆内存:-Xms(初始堆大小)、-Xmx(最大堆大小,建议与-Xms一致避免频繁扩容)。
  • 新生代:-Xmn(新生代大小)、-XX:SurvivorRatio=8(Eden 与 Survivor 比例)。
  • 元空间:-XX:MetaspaceSize-XX:MaxMetaspaceSize
  • GC 日志:-XX:+PrintGCDetails-XX:+PrintGCTimeStamps(分析 GC 问题)。
  • 垃圾收集器:-XX:+UseG1GC(使用 G1)、-XX:+UseZGC(使用 ZGC)。

总结

JVM 是 Java 生态的核心,其内存管理、类加载、垃圾回收和执行引擎共同支撑了 Java 的跨平台性和安全性。理解 JVM 原理不仅有助于排查内存泄漏、OOM 等问题,也是高性能 Java 应用调优的基础。

相关推荐
wangjialelele10 小时前
Linux中的线程
java·linux·jvm·c++
英杰.王12 小时前
JVM实战-G1参数调优
jvm
不务专业的程序员--阿飞12 小时前
JVM无法分配内存
java·jvm·spring boot
Micrle_00712 小时前
jvm类加载过程
jvm
the beard14 小时前
JVM垃圾回收器深度解析:从Serial到G1,探索垃圾回收的艺术
java·jvm
歪歪10015 小时前
如何在SQLite中实现事务处理?
java·开发语言·jvm·数据库·sql·sqlite
过客随尘1 天前
生产环境OOM排障实战
jvm·后端
summer_west_fish2 天前
JVM实际内存占用
jvm
Moshow郑锴2 天前
IDEA/WebStorm 卡顿问题与启动参数调优指南
java·jvm·intellij-idea·webstorm