java基础-jvm内存模型
JVM(Java Virtual Machine,Java虚拟机)的内存模型(运行时数据区域)是其核心组成部分,用于管理程序运行过程中的各类数据。根据《Java虚拟机规范》,JVM的内存结构可分为以下6大区域(部分区域为线程私有,部分为线程共享):
一、程序计数器(Program Counter Register)
线程私有,是一块较小的内存空间,用于记录当前线程正在执行的字节码指令的行号(或Native方法的执行状态)。
- 核心作用:当线程被切换时(如CPU时间片用完),程序计数器用于恢复线程的执行位置(类似“断点续跑”)。
- 特性:
- 唯一不会抛出内存溢出(OOM)的区域;
- 若当前线程执行的是Java方法,计数器存储的是字节码指令地址;若执行的是Native方法(如C/C++实现的本地方法),计数器值为
undefined
。
二、Java虚拟机栈(Java Virtual Machine Stacks)
线程私有,每个线程在创建时会分配一个虚拟机栈,用于存储方法调用的栈帧(Stack Frame)。
- 栈帧结构:每个方法调用对应一个栈帧,包含:
- 局部变量表:存储方法参数、局部变量(基本类型、对象引用);
- 操作数栈:用于字节码指令的运算(如
iadd
指令需从操作数栈取两个数相加); - 动态连接:指向运行时常量池中该方法的引用(用于方法调用的动态绑定);
- 方法出口:记录方法执行完毕后返回到上层方法的地址。
- 异常场景:
- 若线程请求的栈深度超过JVM允许的最大值(如递归过深),抛出
StackOverflowError
; - 若栈可动态扩展(如HotSpot默认固定大小),但扩展时无法申请到内存,抛出
OutOfMemoryError
(OOM)。
- 若线程请求的栈深度超过JVM允许的最大值(如递归过深),抛出
三、本地方法栈(Native Method Stacks)
线程私有,与虚拟机栈功能类似,但用于执行Native方法(如Java调用C/C++实现的方法,如Object.hashCode()
)。
- HotSpot特性:HotSpot虚拟机直接将本地方法栈与虚拟机栈合并(统一管理)。
四、Java堆(Java Heap)
线程共享,是JVM管理的最大内存区域,所有对象实例(如new Object()
)和数组均在此分配内存。
- 核心作用:Java堆是垃圾回收(GC)的主要区域,因此也被称为“GC堆”。
- 内存划分(分代设计):为优化GC效率,堆通常分为:
- 新生代(Young Generation):存放新创建的对象,分为:
- Eden区(对象初始分配的主区域);
- Survivor区(S0、S1,用于存放GC后存活的对象)。
- 老年代(Old Generation/Tenured Generation):存放多次GC后仍存活的对象(如长生命周期的缓存对象)。
- 新生代(Young Generation):存放新创建的对象,分为:
- 异常场景:若堆内存不足且无法扩展,抛出
OutOfMemoryError: Java heap space
。
五、方法区(Method Area)
线程共享,用于存储类的元数据(如类的结构、字段、方法、接口)、运行时常量池(Runtime Constant Pool)、静态变量(static
修饰的变量)等数据。
- JDK版本差异:
- JDK7及之前:方法区用“永久代(PermGen)”实现,内存限制受JVM参数(如
-XX:MaxPermSize
)控制,且与堆共享内存; - JDK8及之后:方法区用“元空间(Metaspace)”实现,内存直接使用本地内存(不受JVM堆限制),参数改为
-XX:MaxMetaspaceSize
(默认无限制,可能导致本地内存溢出)。
- JDK7及之前:方法区用“永久代(PermGen)”实现,内存限制受JVM参数(如
- 运行时常量池(Runtime Constant Pool):
- 是方法区的一部分,存储编译期生成的字面量(如字符串、数字)和符号引用(如类名、方法名);
- 支持动态扩展(如
String.intern()
可将字符串添加到常量池)。
六、直接内存(Direct Memory)
非JVM规范定义区域,但被频繁使用(如NIO的ByteBuffer
)。它通过Unsafe
或ByteBuffer
直接分配堆外内存(本地内存),避免了Java堆与本地内存的拷贝开销。
- 异常场景:若本地内存不足,可能抛出
OutOfMemoryError
(常见于大量使用NIO的应用)。
关键总结:JVM内存模型 vs Java内存模型(JMM)
需注意区分两个易混淆概念:
- JVM内存模型:本文讨论的“运行时数据区域”,关注内存的物理划分(如堆、栈、方法区);
- Java内存模型(JMM,Java Memory Model):JVM定义的一组规则,用于规范多线程间的内存可见性、原子性和有序性(如通过
volatile
、synchronized
实现),是逻辑模型而非物理区域划分。
附:JVM内存参数示例(JDK8+)
实际开发中,可通过JVM参数调整各区域大小(以HotSpot为例):
# 堆内存:最小256M,最大512M
-Xms256m -Xmx512m
# 新生代大小:128M(Eden:S0:S1=8:1:1)
-XX:NewSize=128m -XX:SurvivorRatio=8
# 元空间:最大256M(本地内存)
-XX:MaxMetaspaceSize=256m
# 栈大小:每个线程栈1M(默认1M,减少可增加线程数)
-Xss1m