Skip to content

JVM 架构深度解析

JVM(Java Virtual Machine)是 Java "一次编写,到处运行"的基石。深入理解 JVM,是成为高级 Java 工程师的必经之路。

JVM 整体架构

┌─────────────────────────────────────────────────────┐
│                    Java 源代码 (.java)                │
└──────────────────────┬──────────────────────────────┘
                       │ javac 编译
┌──────────────────────▼──────────────────────────────┐
│                  字节码文件 (.class)                   │
└──────────────────────┬──────────────────────────────┘

┌──────────────────────▼──────────────────────────────┐
│                      JVM                             │
│  ┌─────────────────────────────────────────────┐    │
│  │              类加载子系统                     │    │
│  │  Bootstrap → Extension → Application        │    │
│  └──────────────────┬──────────────────────────┘    │
│                     │                               │
│  ┌──────────────────▼──────────────────────────┐    │
│  │              运行时数据区                     │    │
│  │  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────────┐   │    │
│  │  │方法区 │ │  堆  │ │ 栈  │ │本地方法栈 │   │    │
│  │  │(元空间)│ │      │ │(线程)│ │          │   │    │
│  │  └──────┘ └──────┘ └──────┘ └──────────┘   │    │
│  │              程序计数器(每线程独立)          │    │
│  └──────────────────┬──────────────────────────┘    │
│                     │                               │
│  ┌──────────────────▼──────────────────────────┐    │
│  │           执行引擎                            │    │
│  │  解释器 │ JIT 编译器 │ GC 垃圾回收器          │    │
│  └─────────────────────────────────────────────┘    │
│                                                     │
│  ┌─────────────────────────────────────────────┐    │
│  │         本地方法接口 (JNI)                    │    │
│  └─────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────┘

运行时数据区详解

程序计数器(PC Register)

  • 线程私有,每个线程独立一份
  • 记录当前线程正在执行的字节码指令地址
  • 执行 native 方法时值为 undefined
  • 唯一不会发生 OOM 的区域

Java 虚拟机栈(JVM Stack)

  • 线程私有,生命周期与线程相同
  • 每次方法调用创建一个栈帧(Stack Frame)
栈帧结构:
┌─────────────────────────┐
│      局部变量表           │  存储方法参数和局部变量
├─────────────────────────┤
│      操作数栈            │  执行字节码指令的工作区
├─────────────────────────┤
│      动态链接            │  指向运行时常量池的方法引用
├─────────────────────────┤
│    方法返回地址           │  方法正常/异常退出后的返回位置
└─────────────────────────┘
java
// 演示栈帧
public class StackDemo {
    public static void main(String[] args) {
        // main 方法对应一个栈帧
        int result = add(1, 2);  // 调用 add,压入新栈帧
        System.out.println(result);
    }

    public static int add(int a, int b) {
        // add 方法的栈帧
        // 局部变量表:a=1, b=2
        return a + b;  // 方法返回,栈帧弹出
    }
}

异常:

  • StackOverflowError:栈深度超过限制(递归过深)
  • OutOfMemoryError:栈扩展时内存不足
bash
# 设置栈大小(默认 512KB-1MB)
java -Xss2m MyApp

堆(Heap)

  • 线程共享,JVM 最大的内存区域
  • 所有对象实例和数组都在堆上分配
  • GC 的主要管理区域
堆内存结构(以 G1 GC 为例):
┌─────────────────────────────────────────┐
│                   堆                     │
│  ┌──────────────┐  ┌──────────────────┐ │
│  │   年轻代      │  │      老年代       │ │
│  │ ┌──┐ ┌──┐   │  │                  │ │
│  │ │Eden│S0│S1 │  │  长期存活对象      │ │
│  │ └──┘ └──┘   │  │                  │ │
│  └──────────────┘  └──────────────────┘ │
└─────────────────────────────────────────┘
bash
# 堆内存参数
-Xms512m          # 初始堆大小
-Xmx2g            # 最大堆大小
-Xmn512m          # 年轻代大小
-XX:NewRatio=2    # 老年代:年轻代 = 2:1
-XX:SurvivorRatio=8  # Eden:S0:S1 = 8:1:1

方法区 / 元空间(Metaspace)

  • 线程共享
  • 存储:类信息、常量、静态变量、JIT 编译后的代码
  • Java 8 之前叫永久代(PermGen),Java 8+ 改为元空间(Metaspace)
  • 元空间使用本地内存,不再受堆大小限制
bash
# 元空间参数(Java 8+)
-XX:MetaspaceSize=256m      # 初始元空间大小
-XX:MaxMetaspaceSize=512m   # 最大元空间大小
java
// 运行时常量池(方法区的一部分)
String s1 = "hello";           // 字符串常量池
String s2 = "hello";
System.out.println(s1 == s2);  // true,同一个常量池对象

String s3 = new String("hello");  // 堆上新对象
System.out.println(s1 == s3);     // false

String s4 = s3.intern();          // 返回常量池中的引用
System.out.println(s1 == s4);     // true

对象的创建过程

new MyObject()

1. 检查类是否已加载(类加载子系统)

2. 分配内存
   - 指针碰撞(内存规整时,Serial/ParNew GC)
   - 空闲列表(内存不规整时,CMS GC)
   - TLAB(Thread Local Allocation Buffer)线程本地分配缓冲

3. 初始化零值(int=0, boolean=false, 引用=null)

4. 设置对象头(Mark Word + 类型指针)

5. 执行 <init> 方法(构造函数)

对象内存布局

对象在堆中的布局:
┌─────────────────────────────┐
│         对象头               │
│  ┌──────────────────────┐   │
│  │  Mark Word (8 bytes) │   │  哈希码、GC年龄、锁状态
│  ├──────────────────────┤   │
│  │  类型指针 (4/8 bytes) │   │  指向类元数据
│  └──────────────────────┘   │
├─────────────────────────────┤
│         实例数据              │  字段值
├─────────────────────────────┤
│         对齐填充              │  保证 8 字节对齐
└─────────────────────────────┘
java
// 使用 JOL 查看对象布局
// <dependency>
//   <groupId>org.openjdk.jol</groupId>
//   <artifactId>jol-core</artifactId>
// </dependency>

import org.openjdk.jol.info.ClassLayout;

public class ObjectLayout {
    public static void main(String[] args) {
        Object obj = new Object();
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        // java.lang.Object object internals:
        // OFFSET  SIZE   TYPE DESCRIPTION
        //      0     4        (object header: mark)
        //      4     4        (object header: class)
        //      8     0        (object alignment)
        // Instance size: 8 bytes
    }
}

执行引擎

解释器 vs JIT 编译器

字节码执行方式:

解释执行:逐条解释字节码 → 启动快,执行慢
JIT 编译:热点代码编译为本地机器码 → 启动慢,执行快

混合模式(默认):
  - 启动时解释执行
  - 热点方法(调用次数超过阈值)触发 JIT 编译
  - C1 编译器(Client):快速编译,优化少
  - C2 编译器(Server):慢速编译,深度优化
  - Graal 编译器(Java 10+):实验性,更激进的优化
bash
# JIT 相关参数
-XX:CompileThreshold=10000    # 方法调用多少次触发 JIT(默认 10000)
-XX:+PrintCompilation         # 打印 JIT 编译信息
-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining  # 打印内联信息
-Xint                         # 纯解释模式(关闭 JIT)
-Xcomp                        # 纯编译模式

常用 JVM 诊断命令

bash
# 查看 JVM 进程
jps -l

# 查看 JVM 参数
jinfo -flags <pid>

# 查看堆内存使用
jstat -gc <pid> 1000  # 每秒刷新

# 生成堆转储文件
jmap -dump:format=b,file=heap.hprof <pid>

# 查看线程状态
jstack <pid>

# 可视化工具
jconsole    # JDK 自带
jvisualvm   # JDK 自带(功能更强)
# 推荐:JProfiler、YourKit(商业)、Arthas(开源)

JVM 版本演进

  • Java 8:G1 GC 成为默认,Lambda/Stream,元空间
  • Java 11:ZGC 引入,HTTP Client 标准化,LTS
  • Java 17:Sealed Classes,Pattern Matching,LTS
  • Java 21:Virtual Threads,Sequenced Collections,LTS(当前推荐)

系统学习 Java 生态,深入底层架构