JVM(Java 虚拟机)的介绍

JVM原理

  • [JVM 核心架构与工作流程](#JVM 核心架构与工作流程)
    • [1. 类加载机制(Class Loading)](#1. 类加载机制(Class Loading))
    • [2. 运行时数据区(Runtime Data Areas)](#2. 运行时数据区(Runtime Data Areas))
      • 堆(Heap)
      • [方法区(Method Area):元空间(Metaspace)公共区域](#方法区(Method Area):元空间(Metaspace)公共区域)
      • [虚拟机栈(Java Stack):线程私有](#虚拟机栈(Java Stack):线程私有)
      • [程序计数器(PC Register):线程私有](#程序计数器(PC Register):线程私有)
      • [本地方法栈:支持 JNI(如调用 C/C++ 库)](#本地方法栈:支持 JNI(如调用 C/C++ 库))
  • 执行引擎与性能优化
  • [内存泄漏(Memory Leak) 和 内存溢出(Out of Memory, OOM)](#内存泄漏(Memory Leak) 和 内存溢出(Out of Memory, OOM))
    • 1.如何查看
    • [2. 监控与诊断工具](#2. 监控与诊断工具)
    • [1. 关键 JVM 参数](#1. 关键 JVM 参数)
  • 性能调优实战
    • [1. 关键 JVM 参数](#1. 关键 JVM 参数)
    • [2. 监控与诊断工具](#2. 监控与诊断工具)
    • [3. 常见问题排查](#3. 常见问题排查)
  • [JVM 新特性与趋势(2025)](#JVM 新特性与趋势(2025))
  • 总结

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个 虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现

为什么java可以跨平台 原因就是有JVM

JVM 核心架构与工作流程

1. 类加载机制(Class Loading)

Java编译器(javac).java文件 -->.class文件

而类加载过程:
加载 :查找 .class 文件,生成二进制数据并创建 Class 对象
链接 :包括
验证:检查字节码格式与安全性;
准备:为静态变量分配内存并赋默认值(如 int 初始化为 0);
解析:将符号引用转为直接引用(方法/字段的实际地址)
初始化 :执行静态代码块( 方法),为静态变量赋实际值

这点可以参考static作用里面详细介绍了
类加载器
启动类加载器(Bootstrap) :加载核心库(java.lang 等),由 C++ 实现。
扩展类加载器(Extension) :加载 jre/lib/ext 目录的扩展类。
应用类加载器(AppClassLoader) :加载用户类路径(ClassPath)的类。
自定义加载器:支持热部署等场景

双亲委派模型:优先委派父加载器处理请求,避免核心类被篡改(如 java.lang.String)

2. 运行时数据区(Runtime Data Areas)

运行时数据区分类 以及对应存储的内容

堆(Heap)

存储对象实例数组,分为 新生代(Eden + Survivor)和 老年代。

新生代:新对象分配区,采用复制算法(Minor GC)
老年代:长期存活对象区,采用标记-清除或标记-整理算法(Full GC)

方法区(Method Area):元空间(Metaspace)公共区域

JDK 8 后由 元空间(Metaspace)替代永久代

1.使用本地内存存储了被虚拟机加载的类元信息常量静态变量即时编译器编译后的代码
加载的类元信息:包括类的名称、方法信息、字段信息

方法区存储方法代码 :方法区中存储了方法的字节码指令。当方法被调用时(即方法入口),JVM需要从方法区中读取该方法的字节码指令来执行,它是类级别的,与具体调用无关

2.元空间不在与堆是连续的物理内存,而是改成本内存,理论上只要本地内存足够就不会出现OOM(OutOfMemoryError),从而尽可能避免OOfM

虚拟机栈(Java Stack):线程私有

虚拟机栈(Java Stack)存储的是栈帧(Stack Frame)

在JVM中,每个方法在调用时都会在调用栈上创建一个栈帧(stack frame)。当方法被调用时,栈帧被创建(在入口),当方法返回时(无论是正常返回还是异常返回),栈帧被销毁(出口)

java 复制代码
// 方法入口
public int exampleMethod(int a) {
    if (a < 0) {
        return 0; // 一个出口
    }
    System.out.println("Positive number: " + a);
    return a; // 另一个出口
}

而栈帧包含了:
局部变量表:局部变量表用于存放编译期可知的各种基本数据类型对象引用类型returnAddress类型数据,最小存储单位为变量槽,编译时就已确定,不受程序影响

操作数栈:操作数栈也被称为操作栈,它是一个LIFO栈,编译时就已确定,不受程序影响,操作数栈的每一个元素都可以是Java数据类型,32位数据类型所占栈容量为1,64位数据类型所占栈容量为2

动态连接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接

方法出口:返回地址等信息,控制权返回给调用者的时刻恢复调用者的栈帧和程序计数器

注意:栈深度过大引发 StackOverflowError;扩展失败导致 OutOfMemoryError

方法区是方法的"静态蓝图"存储地(字节码、元数据)

方法入口/出口是动态执行过程:
入口时读取方法区数据创建栈帧 → 执行中依赖方法区指令 → 出口时释放栈帧但保留方法区数据

二者共同构成Java方法执行的完整生命周期。

程序计数器(PC Register):线程私有

记录当前执行指令地址,唯一不会发生 OOM 的区域

本地方法栈:支持 JNI(如调用 C/C++ 库)

主要提供本地方法

本地方法来源区分

如果方法涉及硬件、系统资源或底层调度,通常由操作系统控制

如果方法涉及 Java 运行时环境的管理,通常由 JVM 操控,如调用 C/C++ 库

执行引擎与性能优化

1. 字节码执行

  • 解释器:逐行解释字节码,启动快但效率低。
  • 即时编译器(JIT) :就是静态编译和动态编译
    • C1 编译器:快速编译热点代码(方法调用计数器阈值 ≈1万次),轻度优化。
    • C2 编译器:深度优化热点代码(如逃逸分析、内联优化),生成高效本地机器码 。
    • Falcon JIT(Azul Zulu Prime):基于 LLVM,性能优于传统 C2 。

2. 垃圾回收(GC)机制

  • 分代收集策略
    • 新生代:复制算法(对象从 Eden → Survivor 区复制)。
    • 老年代:标记-清除(碎片多)或标记-整理(碎片少)。
  • 主流垃圾收集器
    • Serial/Parallel:单线程/多线程,适合低延迟或高吞吐场景。
    • CMS:并发标记清除,减少停顿时间(但存在碎片问题)。
    • G1/ZGC:分区域收集,可预测停顿时间,兼顾吞吐与延迟 。
  • GC 触发条件
    • Minor GC:Eden 区满时触发。
    • Full GC:老年代不足、元空间溢出或显式调用 System.gc()

3. 内存分配策略

  • 对象优先分配在 Eden 区:空间不足时触发 Minor GC。
  • 大对象直入老年代:避免在新生代频繁复制(如长数组)。
  • 长期存活对象晋升:对象年龄(Survivor 区存活次数)超阈值(默认 15)进入老年代

4.不同场景选择合适的收集器组合:

收集器 区域 算法 特点
Serial 新生代 标记-复制 单线程,简单高效(Client模式)
Parallel Scavenge 新生代 标记-复制 多线程,吞吐量优先
ParNew 新生代 标记-复制 多线程版Serial,配合CMS使用
CMS 老年代 标记-清除 并发收集,低延迟(JDK9弃用)
G1 全堆 分区域标记-整理 可控停顿时间,JDK9+默认
ZGC 全堆 染色指针+读屏障 <10ms停顿,TB级堆(JDK15+)

内存泄漏(Memory Leak) 和 内存溢出(Out of Memory, OOM)

内存泄漏(Memory Leak)内存溢出(Out of Memory, OOM)是两种不同的内存管理问题
内存泄漏是指程序在申请内存后无法释放已申请的内存空间,导致系统可用内存逐渐减少,比如一个对象被其余对象引用导致垃圾回收器无法回收该对象所占用的内存,内存泄漏可能会随着时间的推移逐渐恶化,最终可能导致程序性能下降或崩溃

内存溢出则是指程序在运行过程中申请的内存超出了系统分配给它的最大内存限制。这可能是由于程序需要处理的数据量过大,或者程序中存在无限循环或递归调用等问题。当发生内存溢出时,程序会无法继续执行,并抛出相应的错误提示

1.如何查看

2. 监控与诊断工具

  • jstat :查看GC统计 ,监控堆内存与 GC 情况(如 jstat -gcutil <pid> 1000
  • jmap :生成堆转储快照(Heap Dump),分析内存泄漏(jmap -dump:format=b,file=heap.bin <pid>

1. 关键 JVM 参数

性能调优实战

1. 关键 JVM 参数

bash 复制代码
-Xms2g -Xmx2g       # 堆初始/最大内存(避免动态扩容)
-XX:NewRatio=2      # 新生代:老年代 = 1:2
-XX:MaxMetaspaceSize=256m  # 限制元空间大小
-XX:+UseG1GC        # 启用 G1 收集器
-XX:MaxGCPauseMillis=200   # 目标最大停顿时间 

2. 监控与诊断工具

  • jstat :查看GC统计 ,监控堆内存与 GC 情况(如 jstat -gcutil <pid> 1000
  • jmap :生成堆转储快照(Heap Dump),分析内存泄漏(jmap -dump:format=b,file=heap.bin <pid>
  • jstack :抓取线程快照,定位死锁(jstack -l <pid>
  • Arthas:阿里开源工具,支持实时诊断 OOM、热更新代码

3. 常见问题排查

  • OOM(OutOfMemoryError)
    • 堆溢出java.lang.OutOfMemoryError: Java heap space → 增大 -Xmx 或分析对象生命周期
    • 元空间溢出java.lang.OutOfMemoryError: Metaspace → 调整 -XX:MaxMetaspaceSize
  • 频繁 Full GC
    • 老年代空间不足 → 检查对象晋升策略或内存泄漏
    • 元空间不足 → 优化类加载或调整元空间大小

JVM 新特性与趋势(2025)

1.虚拟线程(Project Loom)

  • 轻量级线程(非 OS 线程),提升高并发应用性能(如单机支持百万级连接)

2.ZGC 低延迟优化

  • 停顿时间控制在 1ms 内,适用于金融、实时系统 。

3.AOT 编译(GraalVM)

  • 将字节码预先编译为本地机器码,加速启动速度(适合 Serverless 场景)

总结

JVM 作为 Java 生态的基石,通过 类加载内存管理JIT 即时编译GC 回收 实现跨平台与高性能。掌握其原理可有效解决 OOM、GC 停顿等问题,提升系统稳定性。未来趋势聚焦于低延迟(ZGC)、轻量并发(虚拟线程)及原生编译(GraalVM),持续推动 Java 在云原生场景的应用 。