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 中的线程私有区包括程序计数器、虚拟机栈和本地方法栈,用于存储线程私有的数据和方法调用信息。