JVM的内存划分
JVM(Java Virtual Machine)的内存划分主要包括以下几个运行时数据区域:
1. 程序计数器(Program Counter Register)
- 作用:当前线程执行的字节码行号指示器。
- 特点:
- 线程私有(每个线程独立拥有)。
- 执行Java方法时记录字节码指令地址,执行Native方法时为空(
Undefined
)。 - 唯一不会抛出
OutOfMemoryError
的区域。
2. 虚拟机栈(VM Stack)
- 作用:存储方法执行的栈帧(局部变量表、操作数栈、动态链接、方法出口等)。
- 特点:
- 线程私有,生命周期与线程相同。
- 每个方法执行时创建栈帧,方法结束后栈帧销毁。
- 异常:
- 栈深度超过限制时抛出
StackOverflowError
(如递归过深)。 - 动态扩展失败时抛出
OutOfMemoryError
(如-Xss参数设置过小)。
- 栈深度超过限制时抛出
3. 本地方法栈(Native Method Stack)
- 作用:与虚拟机栈类似,但为Native方法服务(如
Object.wait()
等)。 - 特点:
- 线程私有。
- 不同JVM实现可能不同(如HotSpot将其与虚拟机栈合并)。
- 异常:同虚拟机栈,可能抛出
StackOverflowError
或OutOfMemoryError
。
4. 堆(Heap)
- 作用:存储对象实例和数组,是垃圾回收的主要区域。
- 特点:
- 线程共享,所有对象实例和数组都在此分配。
- 可通过
-Xms
和-Xmx
参数调整大小。
- 分区(以HotSpot为例):
- 新生代(Young Generation):
- Eden区:新对象初始分配的区域。
- Survivor区(S0、S1):Eden区满时,存活对象被移至Survivor区。
- 老年代(Old Generation):长期存活的对象进入老年代(如多次GC后仍存活的对象)。
- 永久代/元空间(PermGen/Metaspace):
- 永久代(Java 7及以前):存储类信息、常量池等,固定大小,可能导致
OutOfMemoryError: PermGen space
。 - 元空间(Java 8+):使用本地内存,默认无上限,避免了永久代的内存溢出问题。
- 永久代(Java 7及以前):存储类信息、常量池等,固定大小,可能导致
- 新生代(Young Generation):
- 异常:堆内存不足时抛出
OutOfMemoryError: Java heap space
。
5. 方法区(Method Area)
- 作用:存储已被虚拟机加载的类信息、常量、静态变量等。
- 特点:
- 线程共享。
- 逻辑上属于堆的一部分,但HotSpot在Java 8后用元空间(本地内存)实现。
- 运行时常量池(Runtime Constant Pool):
- 方法区的一部分,存储编译期生成的常量和符号引用。
- 支持运行时动态生成常量(如
String.intern()
)。
- 异常:元空间不足时抛出
OutOfMemoryError: Metaspace
。
6. 直接内存(Direct Memory)
- 作用:不属于JVM运行时数据区,使用
Unsafe
或NIO
的ByteBuffer.allocateDirect()
直接分配堆外内存。 - 特点:
- 不受堆大小限制,但受物理内存和
-XX:MaxDirectMemorySize
参数限制。
- 不受堆大小限制,但受物理内存和
- 异常:内存不足时抛出
OutOfMemoryError
。
总结
区域 | 线程私有 | 存储内容 | 常见异常 |
---|---|---|---|
程序计数器 | ✅ | 字节码行号 | 无 |
虚拟机栈 | ✅ | 方法栈帧(局部变量、操作数栈等) | StackOverflowError |
本地方法栈 | ✅ | Native方法栈帧 | StackOverflowError |
堆 | ❌ | 对象实例、数组 | OutOfMemoryError: Heap Space |
方法区(元空间) | ❌ | 类信息、常量、静态变量 | OutOfMemoryError: Metaspace |
直接内存 | ❌ | 堆外内存(NIO直接缓冲区) | OutOfMemoryError |
补充
- 垃圾回收(GC):主要针对堆和方法区(元空间)。
- JVM参数示例:
-Xms2048m
:堆初始大小2GB。-Xmx2048m
:堆最大大小2GB。-XX:MetaspaceSize=256m
:元空间初始大小256MB。-XX:MaxDirectMemorySize=512m
:直接内存最大512MB。