当前位置: 首页 > news >正文

java-JVM详解

一、JVM 是什么?

定义
JVM(Java Virtual Machine)是一个虚拟计算机,为 Java 字节码提供运行环境。它是 Java “一次编写,到处运行”(Write Once, Run Anywhere)的核心基础,通过屏蔽底层操作系统差异实现跨平台能力。


二、JVM 的核心作用

  1. 跨平台执行
    • .java 文件编译为与平台无关的字节码.class 文件),由 JVM 解释/编译为机器码执行。
  2. 内存管理
    • 自动分配与回收内存(堆、栈等),避免手动管理内存导致的泄露或溢出。
  3. 代码安全
    • 字节码验证器(Verifier)确保代码符合 JVM 规范,防止恶意代码执行。
  4. 运行时优化
    • 即时编译器(JIT)将热点代码编译为本地机器码,提升执行效率。

三、JVM 的架构组成

JVM Architecture
类加载子系统
运行时数据区
执行引擎
本地方法接口
本地方法库
方法区
JVM栈
本地方法栈
程序计数器
解释器
JIT编译器
垃圾回收器
1. 类加载子系统(Class Loader Subsystem)
  • 作用:加载 .class 文件到内存,生成 Class 对象。
  • 加载流程
    1. 加载(Loading):查找字节码并载入内存。
    2. 链接(Linking)
      • 验证(Verification):确保字节码合法。
      • 准备(Preparation):为静态变量分配内存并赋默认值(如 int0)。
      • 解析(Resolution):将符号引用转为直接引用。
    3. 初始化(Initialization):执行静态代码块和静态变量赋值。
2. 运行时数据区(Runtime Data Areas)
区域作用线程安全
堆(Heap)存储对象实例和数组(GC 主战场)共享
方法区(Method Area)存储类信息、常量池、静态变量(JDK8+ 由元空间 Metaspace 实现)共享
JVM 栈(Stack)存储栈帧(局部变量表、操作数栈、动态链接、方法出口)线程私有
本地方法栈支持 Native 方法(如 C/C++ 代码)线程私有
程序计数器记录当前线程执行的字节码位置(唯一不会 OOM 的区域)线程私有
3. 执行引擎(Execution Engine)
  • 解释器(Interpreter):逐行解释执行字节码(启动快,执行慢)。
  • JIT 编译器(Just-In-Time Compiler)
    • 热点代码(频繁执行的方法)编译为本地机器码(执行快)。
    • 优化策略:方法内联(Inlining)、逃逸分析(Escape Analysis)。
  • 垃圾回收器(GC):自动回收堆中无用对象(详见下文)。
4. 本地方法接口(JNI)
  • 提供调用操作系统本地方法(如 native 修饰的方法)的能力。

四、JVM 内存管理详解

1. 堆内存结构
新生代 (Young Generation)      老年代 (Old Generation)
├── Eden(80%)              └── 存放长期存活对象
├── Survivor 0 (S0, 10%)
└── Survivor 1 (S1, 10%)
  • 对象分配流程
    1. 新对象优先分配在 Eden 区
    2. Eden 满时触发 Minor GC,存活对象移到 Survivor 区(S0/S1)。
    3. 对象在 Survivor 区每熬过一次 GC,年龄 +1。
    4. 年龄达到阈值(默认 15)时晋升到老年代
    5. 老年代空间不足时触发 Full GC(STW 时间长)。
2. 垃圾回收算法
算法原理优缺点
标记-清除标记存活对象 → 清除未标记对象简单,但产生内存碎片
复制将存活对象复制到另一块内存 → 清空原区域无碎片,但浪费 50% 空间
标记-整理标记存活对象 → 向一端移动 → 清理边界外内存无碎片,适合老年代
分代收集新生代用复制算法,老年代用标记-清除/整理综合效率最优(商用 JVM 默认)
3. 垃圾收集器对比
收集器区域算法特点
Serial新生代复制单线程,STW 时间长
Parallel Scavenge新生代复制多线程,吞吐量优先
CMS老年代标记-清除并发收集,低延迟(JDK9 废弃)
G1全堆分区 + 标记-整理可预测停顿(JDK9+ 默认)
ZGC全堆染色指针亚毫秒级停顿(JDK15+ 生产可用)

五、JVM 执行引擎工作流程

Java 源码字节码(.class)JVM 执行引擎操作系统javac 编译类加载子系统加载解释器逐行解释执行JIT 编译器优化编译loop[热点代码检测-]执行本地机器码Java 源码字节码(.class)JVM 执行引擎操作系统
关键步骤:
  1. 解释执行:初始阶段由解释器执行字节码。
  2. 热点探测:计数器统计方法调用次数/循环执行次数。
  3. JIT 编译:将热点代码编译为本地机器码(存入 Code Cache)。
  4. 执行优化代码:后续直接执行编译后的机器码。

六、JVM 的跨平台实现原理

javac
.java 源码
.class 字节码
JVM for Windows
JVM for Linux
JVM for Mac
ARM64 机器码
  • 关键设计
    • 字节码是介于 Java 源码和机器码之间的中间表示。
    • 各平台提供专属 JVM 实现,负责将字节码翻译为本地指令。

七、实战:JVM 参数调优示例

1. 堆内存配置
# 设置初始堆大小 1G,最大堆大小 2G
java -Xms1g -Xmx2g -jar app.jar
2. 选择垃圾收集器
# 使用 G1 收集器,目标停顿 200ms
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar app.jar
3. 内存溢出时生成 Dump
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/dump.hprof -jar app.jar

八、常见问题排查工具

工具作用
jps查看 Java 进程 PID
jstat监控堆内存和 GC 情况(如 jstat -gcutil PID
jmap生成堆转储文件(Heap Dump)
jstack导出线程栈信息(排查死锁)
VisualVM图形化监控 JVM 状态

总结:JVM 的核心价值

  1. 跨平台:字节码 + 平台专属 JVM 实现跨操作系统。
  2. 内存安全:自动内存管理避免手动操作错误。
  3. 性能优化:JIT 编译使 Java 接近原生性能。
  4. 生态基石:支撑 Java/Kotlin/Scala 等 JVM 语言生态。

一、JVM本质与核心作用

1.1 JVM是什么?

JVM是一个抽象的计算机器,它通过软件模拟硬件功能,为Java字节码提供执行环境。其核心价值在于:

  • 跨平台性:通过"一次编译,到处运行"解决平台兼容问题
  • 内存管理:自动内存分配与垃圾回收
  • 安全沙箱:字节码验证和安全机制防止恶意代码
  • 性能优化:JIT编译提升执行效率

1.2 JVM核心架构全景图

┌───────────────────────────────┐
│          Java应用程序          │
└──────────────┬────────────────┘│
┌──────────────▼────────────────┐
│          Class文件             │
└──────────────┬────────────────┘│
┌──────────────▼────────────────┐
│      类加载子系统 (ClassLoader)  │
├───────────────────────────────┤
│ 1. 加载 → 2. 链接 → 3. 初始化    │
└──────────────┬────────────────┘│
┌──────────────▼────────────────┐
│       运行时数据区 (Runtime Data Areas) │
├───────────────────────────────┤
│ • 方法区      • 堆              │
│ • JVM栈      • 本地方法栈       │
│ • 程序计数器                   │
└──────────────┬────────────────┘│
┌──────────────▼────────────────┐
│        执行引擎 (Execution Engine) │
├───────────────────────────────┤
│ • 解释器                      │
│ • JIT编译器                   │
│ • 垃圾回收器                   │
└──────────────┬────────────────┘│
┌──────────────▼────────────────┐
│        本地方法接口 (JNI)        │
└──────────────┬────────────────┘│
┌──────────────▼────────────────┐
│        本地方法库 (Native Libraries)│
└───────────────────────────────┘

二、类加载机制深度剖析

2.1 类加载完整流程

加载 Loading
验证 Verification
准备 Preparation
解析 Resolution
初始化 Initialization
2.1.1 加载阶段
  • 二进制流来源
    • 本地文件系统
    • JAR/WAR包
    • 网络资源
    • 运行时生成(动态代理)
    • 数据库
  • 核心任务
    • 生成方法区类型数据结构
    • 创建对应Class对象(堆中)
2.1.2 链接阶段
  • 验证(四阶段验证):

    1. 文件格式验证:魔数0xCAFEBABE
    2. 元数据验证:语义分析
    3. 字节码验证:程序逻辑校验
    4. 符号引用验证:引用有效性检查
  • 准备

    • 静态变量内存分配
    • 设置零值(int=0, boolean=false)
    • 例外:final static常量直接赋值
  • 解析

    • 符号引用 → 直接引用
    • 类/接口解析、字段解析、方法解析
2.1.3 初始化
  • 执行<clinit>()方法
  • 触发条件(主动引用):
    • new实例化对象
    • 访问静态变量/方法
    • Class.forName()反射
    • 初始化子类触发父类初始化

2.2 类加载器体系

ClassLoader loader = String.class.getClassLoader();
System.out.println(loader);  // null (Bootstrap类加载器)// 类加载器层次
┌─────────────────┐
│ Bootstrap<--- 最顶层(C++实现)
├─────────────────┤
│ Platform<--- JDK9+ (Extension)
├─────────────────┤
│ System<---Application
├─────────────────┤
│ Custom ClassLoader │
└─────────────────┘
  • 双亲委派模型代码实现
protected Class<?> loadClass(String name, boolean resolve) {synchronized (getClassLoadingLock(name)) {// 1. 检查是否已加载Class<?> c = findLoadedClass(name);if (c == null) {try {// 2. 委托父加载器if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {}if (c == null) {// 3. 自行加载c = findClass(name);}}if (resolve) resolveClass(c);return c;}
}

三、内存模型深度解析

3.1 运行时数据区完整结构

┌───────────────────────────────┐
│         堆 (Heap)             │<--- 所有线程共享
│ ┌──────────┬────────────────┐ │
│ │ 新生代   │ 老年代          │ │
│ │ ├──────┐ │                │ │
│ │ │Eden  │ │                │ │
│ │ ├──────┤ │                │ │
│ │ │S0    │ │                │ │
│ │ ├──────┤ │                │ │
│ │ │S1    │ │                │ │
│ └┴──────┴─┴────────────────┘ │
├───────────────────────────────┤
│        方法区 (Method Area)    │<--- JDK8+: Metaspace
├───────────────────────────────┤
│   JVM栈 (Java Virtual Machine Stacks) │<--- 线程私有
│   ┌──────────────────────────┐│
│   │        栈帧 (Frame)        ││
│   │ ┌────────┬─────────────┐ ││
│   │ │ 局部变量表            │ ││
│   │ ├────────┼─────────────┤ ││
│   │ │ 操作数栈              │ ││
│   │ ├────────┼─────────────┤ ││
│   │ │ 动态链接              │ ││
│   │ ├────────┼─────────────┤ ││
│   │ │ 返回地址              │ ││
│   │ └────────┴─────────────┘ ││
│   └──────────────────────────┘│
├───────────────────────────────┤
│   程序计数器 (PC Register)     │<--- 当前指令地址
├───────────────────────────────┤
│   本地方法栈 (Native Stack)    │
└───────────────────────────────┘

3.2 堆内存分配策略

  1. 对象优先在Eden分配

    • 新生代占比:Eden(80%) + Survivor0(10%) + Survivor1(10%)
    • -XX:SurvivorRatio=8 调整比例
  2. 大对象直接进老年代

    • -XX:PretenureSizeThreshold=4M(超过4MB直接进老年代)
  3. 长期存活对象晋升

    • 年龄计数器(对象头中)
    • -XX:MaxTenuringThreshold=15(默认15次GC后晋升)
  4. 空间分配担保

    • 老年代连续空间 > 新生代对象总大小
    • 否则触发Full GC

3.3 对象内存布局(64位系统)

┌───────────────────────────────┐
│       对象头 (Header) 16字节     │
├──────────────┬────────────────┤
│   Mark Word (8字节)            │
├──────────────┼────────────────┤
│   Klass Pointer (4字节)        │
├──────────────┼────────────────┤
│   Array Length (4字节,可选)    │
├──────────────┴────────────────┤
│       实例数据 (Instance Data)   │
├───────────────────────────────┤
│        对齐填充 (Padding)        │
└───────────────────────────────┘

Mark Word结构

┌───────────────────────────────┐
│  锁状态        │  存储内容         │
├───────────────┼───────────────┤
│  无锁         │ hashCode(31)   │
│               │ 分代年龄(4)     │
├───────────────┼───────────────┤
│  偏向锁        │ 线程ID(54)      │
│               │ 时间戳(2)       │
├───────────────┼───────────────┤
│  轻量级锁      │ 指向栈中锁记录的指针 │
├───────────────┼───────────────┤
│  重量级锁      │ 指向Monitor的指针 │
├───────────────┼───────────────┤
│  GC标记       │ 空             │
└───────────────┴───────────────┘

四、垃圾回收机制深度解析

4.1 可达性分析算法

GC Roots类型

  1. 虚拟机栈中引用的对象
  2. 方法区中静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中JNI引用的对象
  5. Java虚拟机内部引用
  6. 被同步锁持有的对象

4.2 垃圾收集算法对比

算法实现方式优点缺点适用场景
标记-清除标记存活对象→清除死亡对象简单直接内存碎片CMS老年代
复制内存分两块→存活对象复制到另一块无碎片、高效空间浪费50%新生代
标记-整理标记→向一端移动→清理边界外无碎片移动成本高Serial Old
分代收集新生代复制+老年代标记整理综合最优实现复杂商用JVM默认
分区收集堆划分多个小区独立回收可控停顿时间跨分区引用复杂G1/ZGC/Shenandoah

4.3 经典垃圾收集器对比

收集器分代线程算法特点适用场景
Serial新生代单线程复制简单高效客户端模式
ParNew新生代多线程复制Serial多线程版配合CMS
Parallel Scavenge新生代多线程复制吞吐量优先后台计算
Serial Old老年代单线程标记-整理Serial老年代版客户端模式
Parallel Old老年代多线程标记-整理Parallel Scavenge老年代版吞吐优先应用
CMS老年代并发标记-清除低延迟WEB应用
G1全堆并发标记-整理可预测停顿JDK9+默认
ZGC全堆并发染色指针<10ms停顿超大堆(8TB+)
Shenandoah全堆并发转发指针低延迟RedHat系JDK

4.4 G1收集器工作流程

初始标记 Initial Mark
根区域扫描 Root Region Scan
并发标记 Concurrent Mark
最终标记 Final Mark
筛选回收 Evacuation
  • 核心创新

    • 分区模型(Region,1-32MB)
    • Remembered Set(RSet)记录跨区引用
    • SATB(Snapshot-At-The-Beginning)算法
  • 调优参数

    -XX:+UseG1GC 
    -XX:MaxGCPauseMillis=200  # 目标停顿时间
    -XX:InitiatingHeapOccupancyPercent=45  # 触发并发标记的堆占用率
    -XX:G1HeapRegionSize=16m  # 分区大小
    

五、执行引擎工作原理

5.1 字节码解释执行

// 示例字节码:iadd指令实现
public int add(int a, int b) {return a + b;
}// 对应字节码:
iload_1  // 加载第一个int参数到操作数栈
iload_2  // 加载第二个int参数
iadd     // 栈顶两元素相加
ireturn  // 返回结果

5.2 JIT编译优化技术

  1. 方法内联(Inlining)

    • 将小方法调用替换为方法体
    • -XX:MaxInlineSize=35(默认内联小于35字节的方法)
  2. 逃逸分析(Escape Analysis)

    • 栈上分配:对象未逃逸出方法
    • 锁消除:同步锁仅被单线程访问
    • 标量替换:对象拆分为基本类型
  3. 循环优化

    • 循环展开(Loop Unrolling)
    • 循环剥离(Loop Peeling)
  4. 公共子表达式消除

    • 重复计算只执行一次

5.3 分层编译(Tiered Compilation)

层级编译方式优化程度启动速度
0解释执行最快
1C1简单编译基础优化
2C2完全编译激进优化
3C1带性能监控
4C2带性能监控

启用参数:-XX:+TieredCompilation(JDK8+默认开启)

六、JVM调优实战指南

6.1 内存参数优化

# 堆内存设置
-Xms4g -Xmx4g  # 初始=最大避免动态调整# 新生代设置
-XX:NewRatio=2          # 老年代:新生代=2:1
-XX:SurvivorRatio=8     # Eden:Survivor=8:1# 元空间设置
-XX:MetaspaceSize=256m  
-XX:MaxMetaspaceSize=512m# 直接内存设置
-XX:MaxDirectMemorySize=1g

6.2 GC日志分析

# 启用详细GC日志
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-Xloggc:/path/to/gc.log# 添加时间戳和年龄信息
-XX:+PrintTenuringDistribution

GC日志解读

2023-08-13T14:23:45.731+0800: [GC pause (G1 Evacuation Pause) (young)[Parallel Time: 25.3 ms, GC Workers: 8][GC Worker Start (ms): Min: 23456.7, Avg: 23456.8, Max: 23456.9, Diff: 0.2][Ext Root Scanning (ms): Min: 0.8, Avg: 1.2, Max: 1.8, Diff: 1.0, Sum: 9.6][Update RS (ms): Min: 0.0, Avg: 0.3, Max: 0.5, Diff: 0.5, Sum: 2.4][Processed Buffers: Min: 0, Avg: 1.6, Max: 3, Diff: 3, Sum: 13][Code Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8][Object Copy (ms): Min: 22.5, Avg: 22.8, Max: 23.1, Diff: 0.6, Sum: 182.4][Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8][Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 8][GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1][GC Worker Total (ms): Min: 24.8, Avg: 24.9, Max: 25.0, Diff: 0.2, Sum: 199.1][GC Worker End (ms): Min: 23481.7, Avg: 23481.7, Max: 23481.7, Diff: 0.0][Code Root Fixup: 0.0 ms][Code Root Purge: 0.0 ms][Clear CT: 0.2 ms][Other: 1.5 ms][Choose CSet: 0.0 ms][Ref Proc: 0.5 ms][Ref Enq: 0.0 ms][Redirty Cards: 0.2 ms][Humongous Register: 0.1 ms][Humongous Reclaim: 0.0 ms][Free CSet: 0.3 ms][Eden: 2048.0M(2048.0M)->0.0B(2048.0M) Survivors: 1024.0M->1024.0M Heap: 4096.0M(8192.0M)->3072.0M(8192.0M)][Times: user=0.20 sys=0.01, real=0.03 secs]

6.3 内存泄漏排查

  1. 生成堆转储文件

    jmap -dump:format=b,file=heapdump.hprof <pid>
    
  2. 使用MAT分析步骤

    • 打开heapdump.hprof
    • 查看直方图(Histogram)
    • 查找支配树(Dominator Tree)
    • 分析路径到GC Roots(Path to GC Roots)
    • 检查重复集合(Duplicate Classes)

七、JVM内部机制深度探秘

7.1 锁优化技术

  1. 偏向锁

    • 适用于单线程访问场景
    • 对象头记录线程ID
  2. 轻量级锁

    • 使用CAS避免阻塞
    • 栈帧中创建Lock Record
  3. 锁膨胀

    • 竞争激烈时升级为重量级锁
    • 对象头指向Monitor对象
  4. 自旋锁

    • -XX:PreBlockSpin=10(默认自旋10次)
    • 自适应自旋(JDK6+)

7.2 内存屏障与happens-before

JMM定义的happens-before规则

  1. 程序顺序规则
  2. 监视器锁规则
  3. volatile变量规则
  4. 线程启动规则
  5. 线程终止规则
  6. 中断规则
  7. 终结器规则
  8. 传递性规则

内存屏障类型

屏障类型作用对应指令
LoadLoad保证Load1先于Load2ifence (x86)
StoreStore保证Store1先于Store2sfence (x86)
LoadStore保证Load先于后续Store
StoreLoad保证Store先于后续Loadmfence (x86)

7.3 JIT编译日志分析

# 启用JIT编译日志
-XX:+PrintCompilation
-XX:+UnlockDiagnosticVMOptions
-XX:+PrintInlining# 示例输出:42   3       java.lang.String::hashCode (55 bytes)43   1       java.util.ArrayList::add (29 bytes)45   4       java.util.HashMap::put (65 bytes)   inline (hot)47   2       java.io.FileInputStream::read (0 bytes)   intrinsic

八、JVM发展趋势

8.1 新一代GC对比

特性ZGCShenandoahG1
最大堆大小16TB32TB64GB
停顿目标<1ms<10ms200ms
算法核心染色指针转发指针分区模型
内存开销~2%~5-10%~10-20%
生产就绪JDK15+JDK12+JDK9+默认

8.2 GraalVM创新

  1. 多语言支持

    • Java, JavaScript, Python, Ruby等
  2. 提前编译(AOT)

    native-image -jar app.jar
    
  3. 高性能编译器

    • 替代C2编译器
    • -XX:+UseJVMCICompiler

8.3 Project Loom(纤程)

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {IntStream.range(0, 10_000).forEach(i -> {executor.submit(() -> {Thread.sleep(Duration.ofSeconds(1));return i;});});
}
http://www.dtcms.com/a/331588.html

相关文章:

  • 如何安装 scikit-learn Python 库
  • Azure微软云内网接入问题
  • 大规模调用淘宝商品详情 API 的分布式请求调度实践
  • ant design vue pro 1.7.8 自定义渲染菜单,多页签,keep-alive 详细教程 vue2.x版
  • day33-LNMP
  • PostgreSQL——视图
  • 六十五、【Linux数据库】MySQL表结构 、 MySQL键值
  • 重学JS-003 --- JavaScript算法与数据结构(三)JavaScript 基础调试方法
  • Codeforces 1042 Div3(ABCDEFG)
  • 【科研日常】使用tensorflow实现自注意力机制和交叉注意力机制
  • Java中Record的应用
  • Flink Stream API 源码走读 - socketTextStream
  • Spark Shuffle机制原理
  • STM32HAL 快速入门(七):GPIO 输入之光敏传感器控制蜂鸣器
  • 深入理解管道(下):括号命令 `()`、`-ExpandProperty` 与 AD/CSV 实战
  • Java 大视界 -- Java 大数据在智能家居能耗监测与节能优化中的应用探索(396)
  • 【漏洞复现】WinRAR 目录穿越漏洞(CVE-2025-8088)
  • JavaScript 解构赋值语法详解
  • iOS Sqlite3
  • Playwright初学指南 (3):深入解析交互操作
  • 【完整源码+数据集+部署教程】肾脏病变实例分割系统源码和数据集:改进yolo11-CARAFE
  • 基于机器学习的文本情感极性分析系统设计与实现
  • 华为宣布云晰柔光屏技术迎来重大升级
  • 生产环境sudo配置详细指南
  • 机器学习学习总结
  • 如何选择适合工业场景的物联网网关?
  • 相较于传统AR作战环境虚拟仿真系统,其优势体现在哪些方面?
  • Python小程序1.0版本
  • C++类与对象核心知识点全解析(中)【六大默认成员函数详解】
  • Perforce P4 Git 连接器