1.堆区和方法区是 所有线程共享的。2.栈、本地方法栈、程序计数器是每个线程独有的
在 JVM 的内存模型中,内存区域可以分为线程共享区和线程私有区。
线程共享区是所有线程共享的内存区域,
线程私有区是每个线程独立拥有的内存区域。
下面我们详细分析 JVM 中的线程共享区。
1. 线程共享区的组成
JVM 中的线程共享区包括以下部分:
-
方法区(Method Area)
-
堆(Heap)
2. 方法区(Method Area)
(1)作用
-
方法区用于存储类的元数据信息,包括:
-
类的结构信息(如类名、方法名、字段名等)。
-
运行时常量池(Runtime Constant Pool)。
-
静态变量(Static Variables)。
-
方法字节码(Method Code)。
-
(2)特点
-
方法区是所有线程共享的。
-
方法区的内存不需要连续,可以是物理上不连续的内存空间。
-
方法区的内存回收主要是针对常量池和类型的卸载。
(3)实现
-
在 JDK 8 之前,方法区的实现是 永久代(PermGen)。
-
在 JDK 8 及之后,方法区的实现是 元空间(Metaspace),使用本地内存(Native Memory)存储。
(4)示例
class MyClass {
static int staticVar = 10; // 静态变量存储在方法区
void myMethod() {
System.out.println("Hello, World!"); // 方法字节码存储在方法区
}
}
3. 堆(Heap)
(1)作用
-
堆用于存储对象实例和数组。
-
几乎所有通过
new
关键字创建的对象都分配在堆中。
(2)特点
-
堆是所有线程共享的。
-
堆是 JVM 中最大的一块内存区域。
-
堆的内存回收由垃圾回收器(GC)负责。
(3)分区
-
堆可以分为以下几个区域:
-
新生代(Young Generation):
-
用于存放新创建的对象。
-
分为 Eden 区、Survivor 区(From 和 To)。
-
-
老年代(Old Generation):
-
用于存放长期存活的对象。
-
-
元空间(Metaspace):
-
在 JDK 8 及之后,元空间从堆中分离出来,使用本地内存存储。
-
-
(4)示例
Object obj = new Object(); // 对象实例存储在堆中
int[] arr = new int[10]; // 数组存储在堆中
4. 线程共享区与线程私有区的对比
区域 | 线程共享区 | 线程私有区 |
---|---|---|
方法区 | 存储类的元数据、静态变量、常量池等。 | 无 |
堆 | 存储对象实例和数组。 | 无 |
虚拟机栈 | 无 | 存储方法的局部变量、操作数栈等。 |
本地方法栈 | 无 | 存储本地方法的调用信息。 |
程序计数器 | 无 | 存储当前线程执行的字节码指令地址。 |
5. 总结
线程共享区 | 说明 |
---|---|
方法区 | 存储类的元数据、静态变量、常量池等。 |
堆 | 存储对象实例和数组。 |
6. 一句话总结
JVM 中的线程共享区包括方法区和堆,方法区存储类的元数据和静态变量,堆存储对象实例和数组。
线程私有区
在 JVM 的内存模型中,线程私有区是每个线程独立拥有的内存区域,用于存储线程私有的数据。线程私有区包括以下几个部分:
1. 线程私有区的组成
JVM 中的线程私有区包括以下部分:
-
程序计数器(Program Counter Register)
-
虚拟机栈(Java Virtual Machine Stack)
-
本地方法栈(Native Method Stack)
2. 程序计数器(Program Counter Register)
(1)作用
-
程序计数器用于存储当前线程正在执行的字节码指令的地址。
-
如果当前线程正在执行 Java 方法,程序计数器存储的是虚拟机字节码指令的地址。
-
如果当前线程正在执行本地方法(Native Method),程序计数器的值为空(Undefined)。
(2)特点
-
程序计数器是线程私有的,每个线程都有自己的程序计数器。
-
程序计数器是 JVM 中唯一不会发生内存溢出(OutOfMemoryError)的区域。
(3)示例
public void myMethod() {
int a = 10; // 字节码指令地址存储在程序计数器中
int b = 20;
int c = a + b;
}
3. 虚拟机栈(Java Virtual Machine Stack)
(1)作用
-
虚拟机栈用于存储 Java 方法的调用信息,包括:
-
局部变量表(Local Variable Table):存储方法的局部变量。
-
操作数栈(Operand Stack):用于执行字节码指令时的操作数存储。
-
动态链接(Dynamic Linking):指向运行时常量池的方法引用。
-
方法返回地址(Return Address):存储方法执行完成后的返回地址。
-
(2)特点
-
虚拟机栈是线程私有的,每个线程都有自己的虚拟机栈。
-
虚拟机栈的内存大小可以通过 JVM 参数
-Xss
设置。 -
如果线程请求的栈深度超过虚拟机栈的最大深度,会抛出
StackOverflowError
。 -
如果虚拟机栈无法申请到足够的内存,会抛出
OutOfMemoryError
。
(3)示例
public void myMethod() {
int a = 10; // 局部变量 a 存储在局部变量表中
int b = 20;
int c = a + b; // 操作数栈用于存储 a 和 b 的值
}
4. 本地方法栈(Native Method Stack)
(1)作用
-
本地方法栈用于存储本地方法(Native Method)的调用信息。
-
本地方法是用其他语言(如 C/C++)编写的方法,通过 Java Native Interface (JNI) 调用。
(2)特点
-
本地方法栈是线程私有的,每个线程都有自己的本地方法栈。
-
本地方法栈的内存大小可以通过 JVM 参数设置。
-
如果线程请求的栈深度超过本地方法栈的最大深度,会抛出
StackOverflowError
。 -
如果本地方法栈无法申请到足够的内存,会抛出
OutOfMemoryError
。
(3)示例
public native void myNativeMethod(); // 本地方法的调用信息存储在本地方法栈中
5. 线程私有区与线程共享区的对比
区域 | 线程私有区 | 线程共享区 |
---|---|---|
程序计数器 | 存储当前线程执行的字节码指令地址。 | 无 |
虚拟机栈 | 存储 Java 方法的调用信息。 | 无 |
本地方法栈 | 存储本地方法的调用信息。 | 无 |
方法区 | 无 | 存储类的元数据、静态变量、常量池等。 |
堆 | 无 | 存储对象实例和数组。 |
6. 总结
线程私有区 | 说明 |
---|---|
程序计数器 | 存储当前线程执行的字节码指令地址。 |
虚拟机栈 | 存储 Java 方法的调用信息,包括局部变量表、操作数栈等。 |
本地方法栈 | 存储本地方法的调用信息。 |
7. 一句话总结
JVM 中的线程私有区包括程序计数器、虚拟机栈和本地方法栈,用于存储线程私有的数据和方法调用信息。