Skip to content

JVM(Java Virtual Machine)

JVM 流程

  • 1.创建 JVM 实例(JNI_CreateJavaVM)
  • 2.初始化类加载器(Bootstrap → Ext → App)
  • 3.加载核心类(java.lang.Object, System, Thread 等)
  • 4.执行 main() 方法
  • 5.运行时:解释执行 → JIT编译 → GC回收 → 程序结束
  • 6.销毁 JVM 实例(DestroyJavaVM)

JVM 结构

plain
┌───────────────────────────────────────────────────────────┐
│                   Class Loader Subsystem                  │
│  ┌─────────┐  ┌────────────┐  ┌───────────┐  ┌──────────┐ │
│  │ Loading │  │Verification│  │Preparation│  │Resolution│ │
│  └─────────┘  └────────────┘  └───────────┘  └──────────┘ │
└───────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────┐
│                    Runtime Data Areas                     │
│  ┌───────────┐ ┌─────────┐ ┌─────────┐ ┌────────────┐     │
│  │Method Area│ │  Heap   │ │VM Stack │ │Native Stack│     │
│  │Metaspace  │ │         │ │         │ │            │     │
│  └───────────┘ └─────────┘ └─────────┘ └────────────┘     │
│  ┌─────────┐                                              │
│  │   PC    │                                              │
│  └─────────┘                                              │
└───────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────┐
│                    Execution Engine                       │
│  ┌─────────────┐  ┌───────────────┐  ┌──────────────┐     │
│  │ Interpreter │  │   JIT (C1/C2) │  │      GC      │     │
│  └─────────────┘  └───────────────┘  └──────────────┘     │
└───────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────┐
│                    JNI / Native Method                    │
│         ┌─────────────┐      ┌──────────────────┐         │
│         │   JNI       │      │  Native Method   │         │
│         └─────────────┘      └──────────────────┘         │
└───────────────────────────────────────────────────────────┘

Class Loader Subsystem

加载流程

plain
源代码(.java) → 编译 → 字节码(.class) → 类加载器 → JVM 内存

Runtime Data Areas

plain
┌─────────────────────────────────────────────┐
│             Runtime Data Areas              │
│ ┌─────────────────────────────────────────┐ │
│ │           Thread-Private Areas          │ │
│ │ ┌─────────┐  ┌─────────┐ ┌────────────┐ │ │
│ │ │   PC    │  │VM Stack │ │Native Stack│ │ │
│ │ └─────────┘  └─────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────┐ │
│ │            Thread-shared Areas          │ │
│ │ ┌─────────────────────┐ ┌───────┐       │ │
│ │ │Method Area/Metaspace│ │  Heap │       │ │
│ │ └─────────────────────┘ └───────┘       │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
区域线程作用特点
PC程序计数器私有记录当前线程执行的字节码行号唯一不会发生 OutOfMemory 的区域
VM Stack虚拟机栈私有存储栈帧(局部变量、操作数栈、动态链接等)方法调用和返回,StackOverflowError / OutOfMemory
Native Stack本地方法栈私有为 Native 方法服务与虚拟机栈类似
Heap共享存放对象实例和数组GC 主要区域,分代收集
Method Area/Metaspace方法区共享存储类信息、常量、静态变量、即时编译器编译后的代码JDK 8 前:永久代;JDK 8+:元空间(本地内存)

VM PC

每个线程有独有一个程序计数器,与线程生命周期相同,记录当前线程执行的字节码行号。

当前线程执行的字节码行号指示器,储存方法区字节码指令的地址偏移量,随线程创建而创建销毁而销毁。是实现线程切换、分支跳转、循环控制的关键。

执行 Native 方法时 PC 为 Undefined。Native 方法由操作系统直接执行,使用物理 CPU 的程序计数器(RIP/EIP),JVM 无法也不需跟踪本地代码的执行位置,返回 Java 代码时,JVM 从返回地址恢复 PC。

Metaspace

特性PermGen(JDK 7-)Metaspace(JDK 8+)
位置JVM 堆内部本地内存(Native Memory)
大小限制固定上限(-XX:MaxPermSize默认无上限,受限于物理内存
GC 影响触发 Full GC 清理独立回收,不强制 Full GC
OOM 类型OutOfMemoryError: PermGen spaceOutOfMemoryError: Metaspace
存储内容类元数据、常量池、静态变量仅类元数据(Class Metadata)
内存碎片易产生碎片使用内存池管理,碎片少

结构

plain
┌─────────────────────────────────────────┐
│           本地内存 (Native Memory)        │
├─────────────────────────────────────────┤
│              Metaspace                  │
│  ┌─────────────────────────────────┐  │
│  │        Class Metadata             │  │
│  │  • Klass 结构(类描述符)          │  │
│  │  • 方法元数据(字节码、参数)       │  │
│  │  • 常量池(运行时常量池)           │  │
│  │  • 字段数据、注解                  │  │
│  │  • OOP 映射(对象-类关系)          │  │
│  └─────────────────────────────────┘  │
│  ┌─────────────────────────────────┐  │
│  │      Vtables / Itables            │  │
│  │  (虚方法表、接口方法表)           │  │
│  └─────────────────────────────────┘  │
│  ┌─────────────────────────────────┐  │
│  │       内存管理结构                 │  │
│  │  • VirtualSpaceList(虚拟空间链表) │  │
│  │  • Chunk 内存池(按大小分级)        │  │
│  │  • ClassLoaderData 隔离区          │  │
│  └─────────────────────────────────┘  │
└─────────────────────────────────────────┘
参数说明默认值/建议
-XX:MetaspaceSize初始大小约 20MB(动态调整)
-XX:MaxMetaspaceSize最大上限无限制(建议设置)
-XX:MinMetaspaceFreeRatioGC 后最小空闲比例40%
-XX:MaxMetaspaceFreeRatioGC 后最大空闲比例70%
-XX:CompressedClassSpaceSize压缩类指针空间1GB(上限)

Chunk 分级池

分配策略

  1. 按需求大小匹配对应 Chunk 链表
  2. 无可用 Chunk 时从 VirtualSpaceNode 切割
  3. Node 不足时向 OS 申请新内存(malloc/mmap
plain
┌────────────────────────────────────────┐
│         VirtualSpaceNode               │
│  (从 OS 申请的大块内存,如 2MB/4MB)      │
├────────────────────────────────────────┤
│  ┌────────┬────────┬────────┬───────┐ │
│  │  Chunk │  Chunk │  Chunk │  ...  │ │
│  │  4KB   │  8KB   │  16KB  │       │ │
│  └────────┴────────┴────────┴───────┘ │
│     ↑        ↑        ↑              │
│  ┌──────┐ ┌──────┐ ┌──────┐          │
│  │ 4KB  │ │ 8KB  │ │ 16KB │ ...      │
│  │ 链表  │ │ 链表  │ │ 链表  │          │
│  └──────┘ └──────┘ └──────┘          │
│     按大小分级缓存,减少碎片              │
└────────────────────────────────────────┘

ClassLoader 隔离与回收

回收条件:

  • 该类加载器加载的所有类实例已死
  • 该类加载器本身无引用
  • 触发条件:Metaspace 占用达到 MetaspaceSize 阈值
plain
┌─────────────────────────────────────────┐
│  Bootstrap ClassLoader 数据区            │
│  (核心类库:rt.jar,无回收)              │
├─────────────────────────────────────────┤
│  Application ClassLoader 数据区          │
│  (应用类,GC 时检查卸载)                 │
├─────────────────────────────────────────┤
│  Custom ClassLoader 数据区               │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐  │
│  │ Loader1 │ │ Loader2 │ │ Loader3 │  │
│  │  元数据  │ │  元数据  │ │  元数据  │  │
│  └─────────┘ └─────────┘ └─────────┘  │
│     ↑ 当 ClassLoader 实例被 GC 回收时      │
│     ↑ 对应 Metaspace 区域一并释放          │
└─────────────────────────────────────────┘

metaspace-oomException in thread "main" java.lang.OutOfMemoryError: Metaspace

java
public class MetaspaceLeak {
    static void leak() {
        while (true) {
            // 每次创建新的 ClassLoader 加载类
            URLClassLoader loader = new URLClassLoader(urls);
            Class<?> clazz = loader.loadClass("GeneratedClass");
            // 未关闭 loader,类元数据累积在 Metaspace
        }
    }
}

Heap

  • 作用:存放所有对象实例和数组
  • 线程共享:所有线程共享,需同步机制
  • 垃圾回收:GC主要工作区域
  • OOM:堆内存不足时抛出OutOfMemoryError
plain
┌───────────────────────────────────────────────────────┐
│                       (Heap)                          │
├───────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────┐ ┌──────────┐ │
│ │              Young Gen               │ │ Old Gen  │ │
│ │                1/3                   │ │   2/3    │ │
│ │  ┌─────────┬───────────┬───────────┐ │ │          │ │
│ │  │  Eden   │     S0    │    S1     │ │ │          │ │
│ │  │  8/10   │    1/10   │   1/10    │ │ │          │ │
│ │  └─────────┴───────────┴───────────┘ │ │          │ │
│ └──────────────────────────────────────┘ └──────────┘ │
└───────────────────────────────────────────────────────┘
  • Young Gen
    • Eden:新对象分配区
    • survivor0(S0):存活对象复制区,交替使用
    • S1:同上
  • Old Gen
    • 存放长期存活对象(默认晋升阈值:15)
    • 大对象直接进入

分配内存流程

plain
1. 新对象 → 尝试在Eden分配
   ↓ 空间不足
2. Minor GC → 存活对象移入Survivor区(S0或S1)
   ↓ 对象年龄增长
3. 年龄达阈值(默认15)→ 晋升老年代
   ↓ 老年代空间不足
4. Major GC/Full GC → 清理老年代
   ↓ 仍不足
5. 抛出 OutOfMemoryError: Java heap space

JVM 参数

参数说明default示例
-Xms初始堆大小m * 1/64-Xms512m
-Xmx最大堆大小m * 1/4-Xmx2g
-Xmn年轻代大小``-Xmn256m
-XX:NewRatio老年代/年轻代比例3/1-XX:NewRatio=2(老:新=2:1)
-XX:SurvivorRatioEden/Survivor比例8/2-XX:SurvivorRatio=8
-Xss栈内存大小,影响函数调用深度。例如,1024KB 大约7625次,512 KB 大约3707次linux x64, 1024KB
-XX:+HeapDumpOnOutOfMemoryErrorOOM时生成堆转储调试用

Heap-OOMjava.lang.OutOfMemoryError: Java heap space

问题现象解决方案
内存泄漏堆持续增长,GC无法回收分析Heap Dump,查找未释放引用
大对象过多频繁Full GC优化对象生命周期,使用对象池
年轻代过小频繁Minor GC调大 -Xmn 或调整比例
老年代膨胀Full GC频繁检查静态集合缓存,优化晋升策略

监控与诊断命令

命令用途
jstat -gc <pid>
jmap -heap <pid>

VM Stack

  • 线程私有:每个线程独立栈,生命周期与线程相同
  • 方法执行:存储栈帧(Stack Frame),对应方法调用
  • LIFO结构:后进先出,方法结束自动弹栈
  • StackOverflowError:栈深度超过限制(如无限递归
  • OOM:扩展时无法申请足够内存(较少见)

结构 HotSpot 实现:两栈物理合并,逻辑分离,统一由 -Xss 控制。

plain
┌───────────────────────────────────────┐
│              Java Thread              │
├───────────────────────────────────────┤
│               JVM Stack               │
│ ┌───────────────────────────────────┐ │
│ │ Stack Frame1 │ Stack Frame2 │ ... │ │
│ └───────────────────────────────────┘ │
├───────────────────────────────────────┤
│            Native Method Stack        │
│ ┌───────────────────────────────────┐ │
│ │ Stack Frame1 │ Stack Frame2 │ ... │ │
│ └───────────────────────────────────┘ │
└───────────────────────────────────────┘

栈帧(Stack Frame)结构

plain
┌─────────────────┐
│ Local Variables │
├─────────────────┤
│ Operand Stack   │
├─────────────────┤
│ Dynamic Linking │
├─────────────────┤
│ Return Address  │
├─────────────────┤
│ Extra           │
└─────────────────┘
  • 局部变量(Local Variables):基本数据类型、对象引用、returnAddress。(Slot为单位(32位),long/double占2 Slot)
  • 操作数栈(Operand Stack):字节码指令工作区,数据运算临时存储
  • 动态连接(Dynamic Linking):指向运行时常量池的方法引用
  • 返回地址(Retuen Address):方法执行完毕后的返回位置
  • 附加信息(Extra):调试信息、异常处理表等(可选)

常见问题

栈溢出Exception in thread "main" java.lang.StackOverflowError

java
// 栈帧无限创建,直至溢出
public void recursive() {
    recursive();  
}

内存溢出java.lang.OutOfMemoryError: Out of stack space

java
// -Xss2m 时,2500个线程 ≈ 5GB内存
while(true) {
    new Thread(() -> { sleep(1000); }).start();
}

局部变量表槽位复用

java
public void method() {
    {
        byte[] big = new byte[6 * 1024 * 1024]; // 6MB数组
    } // big作用域结束,但栈帧未结束,Slot可能未复用
    
    int a = 0; // 若编译器优化,复用big的Slot,数组可被GC
    System.gc(); // 效果取决于编译器实现
}

监控与诊断命令

命令用途
jcmd <pid> VM.metaspace查看 Metaspace 详细统计
jcmd <pid> GC.class_stats类加载统计(JDK 8u40+)
jstat -class <pid>类加载/卸载数量监控
arthas / visualvm可视化 Metaspace 趋势

Native Stack

JVM 参数

sh
# 堆内存
-Xms512m          # 初始堆大小 
-Xmx2g            # 最大堆大小
-Xmn256m          # 年轻代大小

# 元空间(JDK 8+)
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m

# 栈内存
-Xss1m            # 每个线程栈大小

# GC 相关
-XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError

JMM, Java Memory Model

抽象规范,定义多线程环境下共享变量的访问规则

  • 原子性:操作不可中断(synchronizedLockAtomic
  • 可见性:一个线程修改对另一个线程可见(volatilesynchronizedfinal
  • 有序性:禁止指令重排序(volatilehappens-before 原则)