内存结构
- 前言
- 简介
- 程序计数器
- 定义
- 作用
- 特点
- 示例
- 应用场景
主页传送门:📀 传送
前言
Java 虚拟机的内存空间由 堆、栈、方法区、程序计数器和本地方法栈五部分组成。
简介
JVM(Java Virtual Machine)内存结构包括以下几个部分:
- 堆区(Heap):堆区是最大的一块内存区域,由所有线程共享。所有的对象实例以及数组都在这块内存中分配。
- 方法区(Method Area):方法区用于存储已被 JVM 加载的类信息、常量、静态变量,以及即时编译器编译后的代码等数据。
- 栈(Stack):每个线程在创建时都会创建一个 JVM 栈,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量、操作数栈、动态链接、方法出口等信息。
- 程序计数器(Program Counter Register):这是一块较小的内存空间,可以看作是当前线程所执行的字节码指令的行号指示器。
- 本地方法栈(Native Method Stack):与 JVM 栈类似,但是本地方法栈主要用于执行本地方法。
图示如下:
JDK 1.8 同 JDK 1.7 比,最大的差别就是:元数据区取代了永久代。元空间的本质和永久代类似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中,而是使用本地内存。
程序计数器
定义
程序计数器(PC 寄存器)是一块较小的内存空间,它是一个专用的寄存器,用于存储当前正在执行的指令在指令序列中的位置。
作用
- 按照顺序依次执行指令序列中的指令,当执行到跳转指令时,需要根据跳转指令的目标地址更新程序计数器的值,以便下一条指令能够正确执行。
- 实现异常处理和中断服务。当程序出现异常或被中断时,程序计数器的值会被保存下来,以便在异常或中断处理结束后能够正确地继续执行程序。
特点
- 线程隔离性:每个线程拥有自己的程序计数器,一个线程无法直接访问和修改另一个线程的程序计数器。
- 内存占用小:程序计数器占用的内存空间非常小,可以忽略不计。
- 无OutofMemoryError:程序计数器是Java虚拟机规范中唯一一个没有规定任何OutOfMemoryError的区域。
- 执行时有值:程序执行的时候,程序计数器有值,其记录的是程序正在执行的字节码的地址。
- 执行本地方法时值为空:执行native本地方法时,程序计数器的值为空。原因是native方法是Java通过jni调用本地C/C++库来实现,非Java字节码实现,所以无法统计。
示例
public class ProgramCounterExample {
public static void main(String[] args) {
ProgramCounter pc = new ProgramCounter();
pc.increment();
System.out.println(pc.getValue()); // 输出 1
pc.decrement();
System.out.println(pc.getValue()); // 输出 0
pc.setValue(pc.getValue() + 1);
System.out.println(pc.getValue()); // 输出 1
}
}
class ProgramCounter {
private int value;
public ProgramCounter() {
value = 0;
}
public void increment() {
value++;
}
public void decrement() {
value--;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
应用场景
- 指令执行控制:程序计数器可以用于实现指令流水线,即每条指令执行时,程序计数器会自动增加,指向下一条要执行的指令。
- 实现跳转:当程序执行到跳转指令时,程序计数器需要根据跳转指令的目标地址更新自身的值,以便下一条指令能够正确执行。
- 实现异常处理和中断服务:当程序出现异常或被中断时,程序计数器的值会被保存下来,以便在异常或中断处理结束后能够正确地继续执行程序。
- 实现多线程:在多线程环境中,每个线程拥有自己的程序计数器,用于记录该线程正在执行的指令序列。
如果喜欢的话,欢迎 🤞关注 👍点赞 💬评论 🤝收藏 🙌一起讨论 你的支持就是我✍️创作的动力! 💞💞💞