JVM系列 之 详解JVM内存分区(线程安全篇)

🌱JVM系列 之 详解JVM内存分区(线程安全篇)

引言

👋 大家好,我是Shio!今天我们要深入探索Java虚拟机(JVM)如何通过内存区域划分和管理来支撑庞大的Java生态并简化开发流程。我们将聚焦于JVM的内存区域、线程间共享与隔离机制以及各个内存区域的具体功能。

一、 JVM内存区域概述

1️⃣ 内存区域与内存模型的区别

首先区分两个概念:内存区域是JVM对物理内存的实际划分;而内存模型更关注数据的可见性和一致性问题。JVM内存区域主要包括五大块:程序计数器虚拟机栈本地方法栈方法区

2️⃣ 线程共享与线程独占

  • 🔄 线程共享内存区域:堆和方法区存放所有线程都能访问的数据(如对象实例和类信息),在这些区域操作时要考虑线程安全。
  • 👤 线程独占内存区域:程序计数器、虚拟机栈和本地方法栈为每个线程单独分配,用于保存当前执行路径局部变量等信息,无需关心线程安全问题。

​ 今天我们着重讲解线程安全的内存区域, 线程不安全的堆和方法区因为内容比较多, 我们放在下一节进行讲解

二、 JVM内存区域详细解析

1️⃣ 程序计数器

💻

程序计数器(Program Counter Register)也叫PC寄存器,每个线程会通过程序计数器记录当前要执行的的字节码指令的地址。

程序计数器类似硬件层面的寄存器,在JVM中记录当前线程执行的字节码指令地址,供执行引擎读取执行。尽管不能直接监控其值,但通过反编译字节码可以观察其工作原理及循环逻辑表现。

在加载阶段,虚拟机将字节码文件中的指令读取到内存之后,会将原文件中的偏移量转换成内存地址。每一条字节码指令都会拥有一个内存地址

  • 程序计数器可以控制程序指令的进行,实现分支、跳转、异常等逻辑

  • 在多线程执行情况下,Java虚拟机需要通过程序计数器记录CPU切换前解释执行到那一句指令并继续解释运行。

注意:程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,所需的空间是固定的大小,开始即会分配足量的空间(本身只有)

它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

2️⃣ 虚拟机栈(Java方法栈)

每个方法调用对应一个栈帧,其中包含四个重要部分:

  • 🔢 局部变量表:由固定数量和类型的slot组成,存储方法参数、局部变量等信息,编译期间即可计算所需空间大小。
  • 📚 操作数栈:作为方法内变量和中间结果的临时存储区域,执行引擎按顺序读取栈顶操作数进行计算,并将结果压回栈顶。
  • 🔗 动态连接:在多态场景下,方法调用依赖运行时常量池中的动态连接,支持运行时类型判断和方法调用。
  • 🚪 返回地址:无论是正常完成还是异常结束,方法退出时需处理返回地址以继续后续指令执行。
  • 异常表

3️⃣ 本地方法栈

🛠️ 本地方法栈服务于非Java语言编写的本地方法(如C/C++),遵循线程独占原则,涉及操作系统接口或高效计算。

三、线程隔离与共享结论

回顾上述内容,我们可以得出结论:

🔒 线程隔离内存区域:由于程序计数器、虚拟机栈和本地方法栈与线程执行状态紧密相关,因此一定是线程独占的。
👥 线程共享内存区域:堆和方法区容纳所有线程可访问的数据,自然成为线程共享资源。

结语与预告

🎉 在本篇博客中,我们深度剖析了JVM内存区域中的程序计数器、虚拟机栈和本地方法栈及其在Java方法执行过程中的作用。敬请期待下期内容,我们将详细介绍剩余的堆和方法区,进一步深化对JVM内存管理机制的理解。感谢阅读,希望您能持续关注我们的更新!👍