JMM

本文深入解析Java内存布局,包括Nativemethodstack、PC、方法区、VMstack、堆等区域特性及作用。阐述Java内存模型(JMM)在解决CPU缓存一致性问题中的角色,探讨其对原子性、可见性和有序性的保障机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java内存结构

  1. Native method stack , 线程私有
  2. PC,线程私有
  3. 方法区,永久代,–(存放,类信息,常量,static,JIT)(信息共享,由于他需要被所有线程读取类信息等),OOM
  4. VM stack,线程私有,(当运行时栈内存超出设定的栈大小发生OOM)
  5. 堆(信息共享,需要被所有线程访问变量)(OOM)

Java内存模型 JMM

用于指导java运行的规范
在这里插入图片描述
在这里插入图片描述
硬件架构:
在这里插入图片描述
CPU缓存一致性问题:
解决方案:
1. 总线加锁 --<总线包括 数据总线 控制总线 等>(BUS ),会降低CPU吞吐量
2. 缓存一致性协议 ,MESI(intel),当CPU在cache中操作数据时,如果该数据是共享变量,数据在cache中读取到寄存器进行修改并更新内存,cache line置无效,其他CPU就从内存中读数据
3. Java内存模型的必要性:规范内存数据和工作空间的交互
4. - 并发编程的3个特征:
* 原子性(要么同时成功要么同时失败,不可分割)、
* 可见性(线程只能操作自己的工作空间数据)、
* 有序性(程序的顺序不一定就是执行顺序,-- 存在编译重排序和CPU指令重排序(和指令操作时间有关)),
+ 重排遵循as-if-seria:在单线程中,重排不影响执行结果。happens-before:在多线程中遵循的重排规则
在这里插入图片描述

JMM对三个特征的保证

  • JMM的原子性:多个原子性的操作合并后,就不具原子性
    * x = 1 具有写原子性:如果是私有数据,那么具有原子性。如果是共享数据,需要将 x 读取到工作空间,再写,就没有原子性
    * y = x 不具有原子性,因为首先要把 x 读取到工作空间(具有原子性),再把 x 值写到 y(具有原子性),但将两个指令合并就不具有原子性
    * i++ 不具有原子性, 1.首先将i读取到工作空间 2. 进行+1 3.刷新到内存, 三个指令合并就不具有原子性
    * z = z+1 不具有原子性

  • 将不具原子性的指令具有原子性的方法:

    • Synchronized
    • Lock
  • JMM的可见性

    • volatile:在JMM上实现MESI协议
    • Synchronized:加锁
    • JUC Lock:
  • Happens-before原则
    * 程序次序原则:
    * 锁定原则:后一次加锁必须等待上一次解锁
    * volatile原则:声明后,禁止指令重排
    * 传递原则:A–B--C 那么 A 必须在 C 前执行

### 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、付费专栏及课程。

余额充值