JVM内存模型

目录

1、什么是JVM?

2、为什么要学习JVM?

(1)​深入理解Java程序的运行机制​

(2)优化程序性能​

(3)​排查线上问题​

(4)​编写高效代码​

(5)​跨平台与语言无关性​

(6)​面试与职业发展​

3、JVM的体系结构

[​3.1 类加载子系统(Class Loader SubSystem)​​](#3.1 类加载子系统(Class Loader SubSystem))

[​​(1) 加载(Loading)​​](#(1) 加载(Loading))

[​​(2) 链接(Linking)​​](#(2) 链接(Linking))

[​​(3) 初始化(Initialization)​​](#(3) 初始化(Initialization))

[3.​2 运行时数据区(Runtime Data Areas)​​](#3.2 运行时数据区(Runtime Data Areas))

[​​(1) 线程私有区域(每个线程独立)​​](#(1) 线程私有区域(每个线程独立))

[​​(2) 线程共享区域(所有线程共用--->非线程安全的)​​](#(2) 线程共享区域(所有线程共用--->非线程安全的))

[​3.3 执行引擎(Execution Engine)​​](#3.3 执行引擎(Execution Engine))

[​​(1) 解释器(Interpreter)​​](#(1) 解释器(Interpreter))

[​​(2) JIT编译器(Just-In-Time Compiler)​​](#(2) JIT编译器(Just-In-Time Compiler))

[​​(3) 垃圾回收(Garbage Collection)​​](#(3) 垃圾回收(Garbage Collection))

[​​(4) 本地方法接口(JNI, Java Native Interface)​​](#(4) 本地方法接口(JNI, Java Native Interface))

[​3.4 数据流向总结​](#3.4 数据流向总结)

[4、在Java 8版本之后的更新](#4、在Java 8版本之后的更新)

[4.​1 类加载子系统的改进​](#4.1 类加载子系统的改进)

[4.​2 运行时数据区的重大变化​](#4.2 运行时数据区的重大变化)

[​​(1) 方法区 → 元空间(Metaspace)​​](#(1) 方法区 → 元空间(Metaspace))

[​​(2) 堆内存与垃圾回收器​](#(2) 堆内存与垃圾回收器)

[​​(3) 线程栈优化​](#(3) 线程栈优化)

[4.​3 执行引擎的增强​](#4.3 执行引擎的增强)

[​4.4 工具链与监控的演进​](#4.4 工具链与监控的演进)

[4.5 ​总结​](#4.5 总结)

[5、新特性探索(Java 8+)](#5、新特性探索(Java 8+))

[模块一:Java 8~Java 11](#模块一:Java 8~Java 11)

[5.​1 模块化(Java 9+)​​](#5.1 模块化(Java 9+))

[5.​2 元空间(Metaspace)取代永久代​](#5.2 元空间(Metaspace)取代永久代)

[5.3 GC 行为变化(CMS 移除)​​](#5.3 GC 行为变化(CMS 移除))

[模块二:Java 11~Java 17](#模块二:Java 11~Java 17)

[​​5.4 低延迟 GC:ZGC 与 Shenandoah​](#5.4 低延迟 GC:ZGC 与 Shenandoah)

[​5.5 GraalVM 原生镜像(AOT 编译)​​](#5.5 GraalVM 原生镜像(AOT 编译))

[​5.6 虚拟线程(Loom 项目,Java 19 正式)​​](#5.6 虚拟线程(Loom 项目,Java 19 正式))

​模块三:新特性详解​

[​5.7 元空间调优​](#5.7 元空间调优)

[​5.8 G1/ZGC 原理​](#5.8 G1/ZGC 原理)

[​5.9 JFR(Java Flight Recorder)诊断​](#5.9 JFR(Java Flight Recorder)诊断)


1、什么是JVM?

  • Java Virtual Machine(Java虚拟机),用来保证Java语言跨平台。
  • Java虚拟机可以看做是一台抽象的计算机,如同真实的计算机那样,它有自己的指令集以及各种运行时内存区域。

JVM模拟了真实计算机的架构,拥有独立的指令集(如字节码指令)和内存管理机制(如堆、栈、方法区等)。这种设计使得Java程序无需直接操作物理硬件,而是通过JVM的统一接口运行,从而实现跨平台性。例如,javac编译生成的字节码(.class文件)是JVM的"指令集",而堆内存用于存储对象实例,栈内存用于方法调用。

  • Java虚拟机与Java语言并没有必然的联系,它只与特定的二进制文件格式(class文件格式)所关联。

JVM的核心规范是处理符合class文件格式的二进制文件,而非特定语言。这意味着任何能编译成合规字节码的语言(如Kotlin、Scala)均可运行在JVM上。例如,Groovy编写的代码编译后也能被JVM执行,体现了JVM的"语言无关性"。

  • Java虚拟机就是一个字节码翻译器,它将字节码文件翻译成各个系统对应的机器码,确保字节码文件能在各个系统正确运行。

JVM通过解释或即时编译(JIT)将字节码转换为目标系统的原生机器码。例如,在Windows和Linux上,同一份.class文件会被JVM分别翻译为x86或ARM指令,确保程序在不同OS上行为一致。这种"一次编译,到处运行"的特性依赖JVM对各平台的适配。

2、为什么要学习JVM?

学习 JVM(Java虚拟机)​ 是Java开发者进阶的必经之路,主要原因包括以下几个方面:

(1)深入理解Java程序的运行机制

  • Java程序不是直接运行在操作系统上,而是通过JVM执行字节码。
  • 了解JVM如何加载类、管理内存、执行字节码等,能帮助你写出更高效、稳定的代码。
  • 例如:理解ClassLoader机制可以避免类加载冲突,优化启动性能。

(2)优化程序性能

  • JVM的内存管理(堆、栈、方法区)、垃圾回收(GC)机制直接影响程序性能。
  • 掌握JVM调优(如调整堆大小、选择合适的GC算法)可以显著提升高并发、大数据量场景下的应用性能。
  • 例如:-Xms-Xmx参数调整堆内存,避免频繁GC导致的卡顿。

(3)排查线上问题

  • 线上环境可能出现OOM(内存溢出)CPU飙升线程死锁等问题,掌握JVM工具(如jstackjmapVisualVM)看懂GC日志就能够快速定位问题。
  • 例如:用jstack分析线程堆栈,找出死锁或阻塞的代码。

(4)编写高效代码

  • 了解JVM内存模型(如逃逸分析栈上分配)可以优化对象创建方式,减少GC压力。
  • 例如:Stringintern()方法可以复用字符串常量池,减少内存占用。

(5)跨平台与语言无关性

  • JVM不仅支持Java,还支持Kotlin、Scala、Groovy等JVM语言,理解JVM有助于学习这些语言的底层机制。
  • 例如:Kotlin的协程底层依赖JVM的线程模型。

(6)面试与职业发展

  • JVM是Java面试的核心考点之一(如GC、类加载、内存模型),掌握JVM能提升竞争力。
  • 高级开发、架构师等岗位通常要求深入理解JVM调优和底层原理。

3、JVM的体系结构

如下图所示,这是一个JVM的体系结构图(基于Java8的环境):

这张JVM体系结构图清晰地展示了Java虚拟机的主要组成部分及其运行流程,主要包括 类加载子系统(Class Loader SubSystem)​运行时数据区(Runtime Data Areas)​执行引擎(Execution Engine)​ 三大部分。下面我们逐一解析每个模块的功能和流程。


3.1 类加载子系统(Class Loader SubSystem)​

负责运行时首次引用类文件时加载.class文件到内存,并转换为JVM可识别的数据结构。类加载过程分为 加载(Loading)→ 链接(Linking)→ 初始化(Initialization)​ 三个阶段:

​(1) 加载(Loading)​
  • 任务 :查找并加载.class文件(二进制字节码)到内存。
  • 类加载器(Class Loader)​
    • Bootstrap Class Loader(启动类加载器)​ :加载JRE/lib下的核心类库(如java.lang.*)。
    • Extension Class Loader(扩展类加载器)​ :加载JRE/lib/ext下的扩展类。
    • Application Class Loader(应用程序类加载器)​ :加载用户程序的类(classpath下的类)。
​(2) 链接(Linking)​
  • 验证(Verify)​ :检查字节码是否符合JVM规范(如魔数0xCAFEBABE),如果验证失败将会收到验证错误。
  • 准备(Prepare)​ :为 静态变量 分配内存并设置默认值(如int初始化为0)。
  • 解析(Resolve)​:将符号引用(如类名、方法名)转换为直接引用(内存地址)。
​(3) 初始化(Initialization)​
  • 执行<clinit>()方法(静态变量赋值、执行静态代码块)。
  • 例如:static int a = 100; 在此阶段赋值。

3.2 运行时数据区(Runtime Data Areas)​

JVM运行时的内存模型,分为 线程私有线程共享 区域:

​(1) 线程私有区域(每个线程独立)​
  • 程序计数器(PC Register)​
    • 记录当前线程执行的字节码指令地址(线程切换后恢复执行位置)。
    • PS:每一个线程都有自己的程序计数器。
  • 虚拟机栈(Stack Area)​
    • 存储 栈帧(Stack Frame)​,每个方法调用对应生成一个栈帧,所有的局部变量都会在栈内存创建,其中栈帧包含:
      • 局部变量表(LVA)​:存放方法参数和局部变量。
      • 操作数栈(Operand Stack)​ :用于计算中间结果(如i++操作)。
      • 动态链接(Dynamic Linking)​:指向运行时常量池的方法引用。
      • 方法返回地址:方法执行完毕后返回的位置。
  • 本地方法栈(Native Method Stack)​
    • native方法(如C/C++代码)提供运行环境。
    • PS:对应的,每个线程都会创建自己的本地方法栈
​(2) 线程共享区域(所有线程共用--->非线程安全的)​
  • 堆(Heap Area)​
    • 存储 所有对象实例和数组,是垃圾回收(GC)的主要区域。
    • 分为 新生代(Young Gen)​老年代(Old Gen)​
  • 方法区(Method Area)​
    • 存储 类信息、常量、静态变量、JIT编译后的代码 (JDK 8后由 元空间Metaspace 实现)。
    • PS:每个Java虚拟机都只有一个方法区,所以他是公共的。

3.3 执行引擎(Execution Engine)​

负责执行字节码,核心组件包括:

​(1) 解释器(Interpreter)​
  • 逐行解释执行字节码(启动快,但执行效率低)。
  • 当一个方法被调用多次的时候,他还是会逐一解释,所以JIT编译器应运而生。
​(2) JIT编译器(Just-In-Time Compiler)​
  • 热点代码优化:将频繁执行的字节码(比如上文提到的多次调用一个方法的重复代码)编译为本地机器码(本地代码会直接用于系统调用,从而提升性能)。
  • 工作流程
    • Profiler(分析器)​:识别热点代码(如循环、高频调用方法)。
    • Code Optimizer(代码优化器)​:优化字节码(如内联、逃逸分析)。
    • Target Code Generator(目标代码生成器)​:生成机器码。
​(3) 垃圾回收(Garbage Collection)​
  • 自动回收堆内存中的无用对象(如引用计数、可达性分析算法)。
  • 我们可以通过调用**System.gc()方法触发垃圾回收器,但是不能保证执行。**
​(4) 本地方法接口(JNI, Java Native Interface)​
  • 调用本地方法(如System.currentTimeMillis()底层用C实现)。

3.4 数据流向总结

  1. 类加载.class文件 → 类加载子系统 → 方法区(存储类信息)。
  2. 运行阶段
    • 线程栈执行方法 → 操作数栈计算 → 堆内存创建对象。
    • JIT优化热点代码 → 生成机器码加速执行。
  3. 垃圾回收:堆内存中的无用对象被GC清理。

4、在Java 8版本之后的更新

4.​1 类加载子系统的改进

模块 Java 8 Java 11/17
类加载器 Bootstrap/Extension/Application 模块化系统(Java 9+)​ ​:引入 Layer 分层加载机制,支持动态模块依赖。
加载过程 固定路径(JRE/lib, ext, classpath) 模块路径(module-path)​​ 替代 classpath,增强隔离性。
元数据存储 永久代(PermGen)存储类元数据 元空间(Metaspace)​ ​:类元数据移至本地内存,默认无上限(-XX:MaxMetaspaceSize 可限制)。

影响​:

  • 永久代 OOM 问题消失,但需监控元空间占用(如滥用动态类生成仍会触发 Metaspace OOM)。
  • 模块化减少类冲突(如 javax vs jakarta),但需适应新打包方式(jlink 生成定制化运行时)。

4.​2 运行时数据区的重大变化

​(1) 方法区 → 元空间(Metaspace)​
java 复制代码
- Java 8: Method Area (PermGen)
  - 固定大小(`-XX:PermSize=64m`),易触发 `OutOfMemoryError: PermGen space`。
  - 存储:类元数据、运行时常量池、静态变量。

+ Java 17: Metaspace
  - 本地内存管理,默认自动扩展(受限于物理内存)。
  - 存储:仅类元数据(静态变量移至堆中)。
  - 调优:`-XX:MaxMetaspaceSize=256m` 限制上限。
​(2) 堆内存与垃圾回收器
特性 Java 8 Java 11/17
默认 GC Parallel GC(吞吐优先) G1 GC​(平衡吞吐/延迟)
低延迟 GC ZGC ​(亚毫秒级停顿,Java 11+) ​Shenandoah​(低停顿,Java 12+)
堆分区 固定新生代/老年代比例 G1 的 ​Region-Based​ 动态分区

影响​:

  • ZGC/Shenandoah 适用于微服务、实时系统(如支付交易),但需显式启用:

    java 复制代码
    java -XX:+UseZGC -jar app.jar
  • G1 的默认化减少调优负担,但需理解 RegionMixed GC 机制。

​(3) 线程栈优化
  • Java 15+ 虚拟线程(预览)​

    • 轻量级线程(JEP 425),减少栈内存占用和上下文切换开销。
    • 替代传统 Thread 模型,适用于高并发(如 10W+ 连接)。
    java 复制代码
    Thread.startVirtualThread(() -> {...}); // Java 19 正式发布

4.​3 执行引擎的增强

组件 Java 8 Java 11/17
JIT 编译器 C1/C2 分层编译 Graal JIT​(Java 10+ 可选)
AOT 编译 不支持 GraalVM AOT​(生成原生镜像,Java 17+)
本地方法 传统 JNI Project Panama​(预览,简化 FFI 调用)

影响​:

  • Graal JIT:更激进的优化(如逃逸分析),但可能增加编译时间。

  • AOT 编译 :提升启动速度(如 Spring Native),但牺牲动态性(反射需配置)。

    java 复制代码
    native-image -jar app.jar  # 生成原生可执行文件

​4.4 工具链与监控的演进

工具 Java 8 Java 11/17
监控工具 jstat、jmap、VisualVM JDK Mission Control (JMC)​ ​ ​Java Flight Recorder (JFR)​
命令行 jcmd 功能有限 增强版 jcmd(支持 Metaspace/ZGC 诊断): jcmd <pid> VM.metaspace
日志 GC 日志需手动解析 统一日志(-Xlog)​ ​: -Xlog:gc*=debug:file=gc.log

示例(Java 17 ZGC 日志分析)​​:

java 复制代码
java -XX:+UseZGC -Xlog:gc*=info:file=zgc.log -jar app.jar

4.5 ​总结

Java 11/17 的 JVM 在 ​内存模型 ​(元空间)、垃圾回收 ​(ZGC)、执行效率 ​(Graal/AOT)和 ​可观测性​(JFR)上大幅改进,但核心架构(类加载、堆栈模型)仍与 Java 8 一脉相承。


5、新特性探索(Java 8+)

模块一:Java 8~Java 11

5.​1 模块化(Java 9+)​
  • 问题背景
    图中 Class Loader SubSystemBootstrap/Extension/Application 三层加载器在 Java 8 中依赖 classpath,易引发 JAR 地狱(如版本冲突)。
  • Java 11 解决方案
    • 模块化系统(JPMS)​
      • 类加载改为 模块路径(module-path)​ 替代 classpath,每个模块声明依赖(module-info.java)。
      • 新增 Layer 分层加载机制,支持动态模块化部署。
    • 影响
      • 需重构大型项目为模块(如拆分 com.example 为独立模块)。

      • 工具链变化:jlink 生成定制化运行时,剔除未用模块减小体积。

        java 复制代码
        jlink --add-modules java.base --output minimal-jre
5.​2 元空间(Metaspace)取代永久代
  • ​与上图对比
    原图的 Method Area(永久代)在 Java 8 中通过 -XX:PermSize 固定大小,易触发 OutOfMemoryError: PermGen space
  • Java 11 变化
    • 元空间(Metaspace)特性

      • 类元数据移至 本地内存,默认无上限(受限于物理内存)。
      • 字符串常量池、静态变量移至 堆内存
    • 调优参数

      java 复制代码
      -XX:MaxMetaspaceSize=256m  # 限制上限
      -XX:MetaspaceSize=64m      # 初始大小
    • 监控命令

      java 复制代码
      jcmd <pid> VM.metaspace    # 查看元空间使用详情
5.3 GC 行为变化(CMS 移除)​
  • 图中对比
    原图 Execution EngineGarbage Collection 未明确收集器类型,Java 8 默认 Parallel GC,CMS 为老年代低停顿选项。
  • Java 11 更新
    • CMS 移除 :改用 G1(Garbage-First)​ 作为默认收集器。

    • G1 核心改进

      • Region-Based 堆分区:取代固定新生代/老年代比例,动态调整 Region 用途。
      • Mixed GC 模式:同时回收新生代和老年代 Region。
    • 调优示例

      java 复制代码
      -XX:+UseG1GC -XX:MaxGCPauseMillis=200  # 目标停顿时间

模块二:Java 11~Java 17

​​5.4低延迟 GC:ZGC 与 Shenandoah
  • ZGC(Java 11+)​

    • 目标:亚毫秒级停顿(<1ms),适合实时交易系统。

    • 原理

      • 染色指针(Colored Pointers):在指针中存储元数据,加速标记。
      • 并发压缩:无需停顿线程整理内存。
    • 启用命令

      java 复制代码
      -XX:+UseZGC -Xmx16g -Xlog:gc*=info:file=zgc.log
  • Shenandoah(Java 12+)​

    • 类似 ZGC,但通过 Brooks 指针实现并发压缩,适用于大堆(>32GB)。
​5.5GraalVM 原生镜像(AOT 编译)​
  • 图中对比
    原图 Execution EngineJIT Compiler(C1/C2)在 Java 17 中可替换为 Graal JITAOT 编译
  • 原生镜像特性
    • 将 Java 程序编译为独立可执行文件(无需 JVM),启动速度提升 10x。

    • 限制 :反射、动态类加载需提前配置(reflect-config.json)。

    • 示例

      java 复制代码
      native-image -jar app.jar --no-fallback
​5**.6 虚拟线程(Loom 项目,Java 19 正式)​**​
  • 背景
    原图的 Stack Area 中每个线程固定占用 ~1MB 栈内存,高并发时成本太高了。
  • 虚拟线程
    • 轻量级线程(协程),由 JVM 调度,复用 OS 线程。

    • 支持百万级并发(传统线程仅千级)。

    • 示例

      java 复制代码
      Thread.startVirtualThread(() -> System.out.println("Virtual Thread"));

模块三:新特性详解

​5**.7 元空间调优**​
  • 监控工具
    • JMC/JFR:实时监控 Metaspace 使用率与 GC 事件。
    • Arthas:诊断类加载泄漏(如动态生成类未卸载)。
  • 常见问题
    • Metaspace OOM:因滥用 CGLIB/ASM 动态生成类,需限制大小或优化代码。
​5**.8 G1/ZGC 原理**​
收集器 G1(Java 8+)​ ZGC(Java 11+)​
目标 平衡吞吐与停顿(~200ms) 亚毫秒级停顿(<1ms)
堆布局 固定大小 Region 动态 Region(无分代)
标记 并发标记 + SATB 算法 并发染色指针
压缩 并行压缩(Mixed GC 阶段停顿) 并发压缩(无停顿)
​5**.9 JFR(Java Flight Recorder)诊断**​
  • 启用命令

    java 复制代码
    -XX:StartFlightRecording=delay=5s,duration=60s,name=myrecording,filename=recording.jfr
  • 关键事件

    • jdk.GCPhase:分析 GC 停顿时间。
    • jdk.MetaspaceAllocationFailure:定位元空间问题。
  • 工具

    • JMC 可视化分析 .jfr 文件。

通过对比原图与新版特性,可以清晰看到 JVM 从 ​固定内存模型 ​ 向 ​动态化、低延迟、云原生​ 的演进方向。

相关推荐
chxii1 小时前
5java集合框架
java·开发语言
yychen_java2 小时前
R-tree详解
java·算法·r-tree
JANYI20182 小时前
嵌入式设计模式基础--C语言的继承封装与多态
java·c语言·设计模式
xrkhy3 小时前
反射, 注解, 动态代理
java
Ten peaches3 小时前
Selenium-Java版(操作元素)
java·selenium·测试工具·html
lyw2056193 小时前
RabbitMQ,Kafka八股(自用笔记)
java
邹诗钰-电子信息工程3 小时前
嵌入式自学第二十一天(5.14)
java·开发语言·算法
有梦想的攻城狮3 小时前
spring中的@MapperScan注解详解
java·后端·spring·mapperscan
寒小松4 小时前
Problem E: List练习
java·数据结构·list
zimoyin4 小时前
Kotlin 协程实战:实现异步值加载委托,对值进行异步懒初始化
java·前端·kotlin