JMM——运行时数据区

Java虚拟机运行时数据区




1 程序计数器(Program Counter Register)

  1.1 程序计数器是一块较小的线程私有的内存空间,可以看作是当前线程锁执行的字节码的行号指示器。

  1.2 如果线程正在执行的是一个Java方法,记录的是正在执行的虚拟机字节码指令的地址

  1.3 如果执行的是Native方法,计数器值为空(Undefined)

  1.4 程序计数器是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域


2 Java虚拟机栈

  2.1 线程私有的生命周期和线程相同的内存区域。

  2.2 虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息 

  2.3 每一个方法从调用直至执行完成的过程,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程

  2.4 long和double类型的数据会占用2个局部变量空间(Slot)

  2.5 局部变量表所需的内存空间在编译期间完成分配,在方法运行期间不会改变局部变量表的大小

  2.6 栈中存放的是基本数据类型和堆中对象的引用(4byte


栈帧的结构图:


3 本地方法栈

同上,区别就是执行的是本地方法


4 Java堆

在虚拟机启动时创建

Java堆是被所有线程共享的一块内存区域也是Java虚拟机锁管理的内存中最大的一块

Java堆中存放的是对象


5 方法区

和堆一样是各个线程共享的内存区域

用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据


6 运行时常量池(Runtime Constant Pool)

是方法区的一部分。

存放编译器生成的各种字面量和符号引用


7 直接内存(Direct Memory)

直接内存并不是虚拟机运行时数据区的一部分,使用Native函数库直接分配堆外内存

使用DirectByteBuffer对象作为这块内存的引用进行操作











03-22
### JMM 的基本概念 Java 内存模型JMM, Java Memory Model)是 Java 虚拟机的一部分,用于定义多线程程序在共享内存中的交互规则。它的核心目标在于为线程间操作提供一致的行为规范,确保多线程环境下数据的一致性和可见性[^1]。 JMM 不仅是一个抽象的概念框架,还涉及具体的实现细节,比如如何利用硬件特性(如缓存一致性协议、内存屏障等),从而保障多线程程序的正确运行[^2]。 --- ### JMM 的主要原理 #### 1. **内存分区结构** JMM 定义了一个抽象的内存结构,其中主要包括主内存(Main Memory)和工作内存(Working Memory)。 - 主内存存储的是实例对象及其字段的实际值。 - 工作内存则是每个线程私有的区域,保存该线程使用的变量副本。当线程读取某个变量,会先从主内存加载到自己的工作内存;写入,则需将更新后的值刷新回主内存[^3]。 这种设计使得不同线程之间无法直接访问彼此的工作内存,而必须通过主内存进行通信。 #### 2. **三大特性** ##### (1) **原子性** 指不可分割的操作,在同一刻只能由一个线程完成。然而需要注意的是,某些看似简单的操作可能并非真正意义上的原子操作。例如 `i++` 实际上包含了三个步骤:读取 i 的当前值、计算新值并将其写回。如果多个线程同执行此语句,就可能出现竞态条件[^4]。 为了保证复杂操作的原子性,通常借助锁机制(如 synchronized 或 ReentrantLock)或者无锁算法(如 CAS 循环比较交换技术)来实现。 ##### (2) **可见性** 指的是当一个线程修改了某共享变量后,其他线程能够立即感知这一变化的能力。由于现代计算机体系架构中存在高速缓存层,可能导致最近一次更改尚未同步至主存便被另一个 CPU 核心所获取的情况发生。为此,JMM 提供了一些关键字支持,像 volatile 就是用来强制指定每次对该标记位属性的访问都必须直接作用于主存之上,而不是依赖本地拷贝版本。 ##### (3) **有序性** 即使源码层面按照既定顺序书写逻辑流程,但在实际编译优化过程中仍可能发生指令重排现象——即调整原本应该遵循先后次序的动作序列以便提升效率。不过这往往会造成意想不到的结果特别是在跨线程协作场景下尤为明显。因此引入 happens-before 关系作为约束手段之一,明确规定哪些情形之下允许打破常规排列规律而不影响最终效果评估标准。 以下是几个典型的 happens-before 规则: - 程序顺序规则:单一线程内的动作保持原有声明位置关系; - 监视器锁定规则:解锁前发生的事件必定早于后续加锁行为之前; - Volatile 变量规则:针对同一个 volatile 类型成员项而言,前面对其所做的任何改动必然优先反映给后来查询者看到的内容; - 线程启动/终止规则:新建子进程正式投入运转之后才能看见父进程中已完成初始化部分的状态设定情况同样适用于结束阶段处理完毕信号传递过程等等。 --- ### 示例代码展示 下面给出一段简单示例演示上述理论知识点的应用: ```java public class VisibilityExample { private static boolean ready; private static int number; public static void main(String[] args) throws InterruptedException { new Thread(() -> { while (!ready); // spin-wait until flag is set to true by another thread. System.out.println("Number: " + number); }).start(); Thread.sleep(100); number = 42; // Write operation on shared variable 'number'. ready = true; // Set the readiness indicator which should be visible across threads. /* Without proper synchronization or using a memory barrier such as declaring 'ready' as volatile, there's no guarantee that changes made here will become immediately apparent elsewhere */ } } ``` 在此例子当中如果没有采用适当措施的话很可能造成输出结果不符合预期因为第二个线程未必能及察觉第一个线程已经改变了相应数值状态。 --- ### 总结 综上所述,深入理解 JMM 对开发人员来说非常重要,尤其是在构建高性能并发应用程序的候。掌握好其背后的运作机理有助于我们更好地规避潜在风险点进而提高整体软件质量水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值