JVM整体结构及内存模型
JVM内存模型定义了Java程序运行时数据的存储区域,共分为六大区域:堆、方法区(元空间)、虚拟机栈、本地方法栈、程序计数器、直接内存。其中堆和方法区线程共享,其他为线程私有。
一、运行时数据区域概览

二、各区域详细说明
1. 堆内存(Heap)
堆内存(Heap)是 JVM 管理的最大的一块内存区域,被所有线程共享,专门用于存储对象实例和数组。
| 属性 | 说明 |
|---|---|
| 存储内容 | 对象实例、数组 |
| 线程共享 | 是 |
| 生命周期 | JVM启动时创建,JVM关闭时销毁 |
| GC管理 | 是,主要回收区域 |
堆的分代结构:

2. 方法区(Method Area)/ 元空间(Metaspace)
元空间(Metaspace)是 JDK 8 开始用来存储类元数据(如类名、方法信息、字段信息、常量池等)的内存区域,它使用本地内存(Native Memory)而非虚拟机内部内存,从而避免了经典的 OutOfMemoryError: PermGen space 问题。
| 属性 | 说明 |
|---|---|
| 存储内容 | 类元信息、静态变量、常量池、方法字节码 |
| 线程共享 | 是 |
| 位置变化 | Java 7:永久代;Java 8+:元空间(本地内存) |
| GC管理 | 部分(常量池回收、类卸载) |
3. 栈内存(Stack)
栈(JVM Stack)是线程私有的内存空间,而栈帧(Stack Frame)是栈中的元素/单元。可以理解为:栈是载体容器,栈帧是放入其中的具体内容。
| 属性 | 说明 |
|---|---|
| 存储内容 | 栈帧(局部变量表、操作数栈、动态链接、方法出口) |
| 线程私有 | 是 |
| 生命周期 | 与线程相同 |
| 异常 | StackOverflowError、OutOfMemoryError |
生成时机:栈帧在每次调用方法时创建,压入当前线程的栈顶。
销毁时机:方法正常返回或异常结束时销毁,从栈顶弹出。
每个栈帧包含:
-
局部变量表:存储方法参数和局部变量
-
操作数栈:存放计算过程中的中间结果
-
动态链接:指向运行时常量池的符号引用
-
方法出口:方法执行后返回的地址
4. 程序计数器(PC Register)
程序计数器是一块很小的内存区域,每个线程私有,用于存储当前线程所执行的字节码的行号指示器(即下一条要执行的指令地址)。
| 属性 | 说明 |
|---|---|
| 存储内容 | 当前线程执行的字节码行号 |
| 线程私有 | 是 |
| 唯一无OOM区域 | 不会出现OutOfMemoryError |
5. 本地方法栈(Native Method Stack)
本地方法栈是一块线程私有的内存区域,专门用于支持 Native 方法(如使用 C/C++ 编写的底层方法)的执行,为这些方法提供服务(如局部变量存储、操作数栈等)。
| 属性 | 说明 |
|---|---|
| 存储内容 | 本地方法(Native方法)的执行信息 |
| 线程私有 | 是 |
| 作用 | 为JVM执行Native方法服务 |
6. 直接内存(Direct Memory)
直接内存是 NIO(New I/O)中引入的一块特殊内存区域,它不在 JVM 堆内管理,而是由操作系统直接分配和管理的本地内存,用于提高 I/O 操作的性能(避免数据在 JVM 堆和本地内存之间来回拷贝)。
| 属性 | 说明 |
|---|---|
| 存储内容 | NIO直接缓冲区 |
| 分配方式 | 通过ByteBuffer.allocateDirect() |
| 特点 | 不受堆大小限制,但受物理内存限制 |
三、内存区域对比
| 区域 | 线程共享 | 存储内容 | GC回收 | OOM风险 |
|---|---|---|---|---|
| 堆 | 是 | 对象实例 | 是 | 高 |
| 方法区/元空间 | 是 | 类元数据、静态变量 | 部分 | 中 |
| 虚拟机栈 | 否 | 局部变量、操作数栈 | 否 | StackOverflow |
| 程序计数器 | 否 | 字节码行号 | 否 | 无 |
| 本地方法栈 | 否 | Native方法信息 | 否 | 中 |
| 直接内存 | 否 | NIO缓冲区 | 否 | 中 |
四、常见问题
1. 内存溢出场景
| 异常 | 产生区域 | 原因 |
|---|---|---|
| OutOfMemoryError: Java heap space | 堆 | 对象过多,GC无法回收 |
| OutOfMemoryError: Metaspace | 元空间 | 加载类过多 |
| StackOverflowError | 栈 | 递归过深 |
| OutOfMemoryError: Direct buffer | 直接内存 | NIO缓冲区未释放 |
2. 参数配置
# 堆内存
-Xms2g # 初始堆大小
-Xmx2g # 最大堆大小
-Xmn512m # 年轻代大小
# 元空间(Java 8+)
-XX:MetaspaceSize=256m # 初始元空间大小
-XX:MaxMetaspaceSize=256m # 最大元空间大小
# 栈内存
-Xss1m # 每个线程栈大小
五、核心要点总结
-
堆和方法区是线程共享的,存在线程安全问题
-
栈和PC寄存器是线程私有的,天然线程安全
-
Java 8将永久代改为元空间,使用本地内存,避免OOM
-
程序计数器是唯一不会OOM的内存区域
-
栈内存每个方法调用都会创建栈帧,方法结束自动释放
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)