java内存模型(jmm)是构建在mesi协议之上的抽象层,通过volatile、synchronized等语义约定触发内存屏障,由jvm转译为底层指令来兑现mesi硬件一致性;它不定义物理缓存,只规定happens-before关系以保证可见性,而mesi负责缓存行状态一致但不保证执行顺序,store buffer和invalidation queue导致的延迟需jmm屏障精准干预。

Java 内存模型(JMM)不是替代硬件,而是站在 MESI 协议肩膀上构建的抽象层——它不直接操作缓存行,而是通过语义约定(如 volatile、synchronized)告诉 JVM 在哪插入内存屏障,再由 JVM 转译为能触发 MESI 行为的底层指令。
JMM 把硬件能力“翻译”成可编程规则
JMM 不定义物理缓存怎么工作,而是规定:哪些操作之间必须存在 happens-before 关系,才能保证一个线程的写对另一个线程可见。这个“保证”,最终要靠 CPU 硬件兑现。MESI 是兑现的基础,但本身不提供编程接口;JMM 则把 MESI 能做到的事,包装成开发者能理解的语义:
- volatile 写 → JMM 要求“立即可见”,JVM 就生成 lock addl 或 stlr 指令 → 触发 RFO 广播 → 其他核将对应缓存行置为 I 状态
- synchronized 退出 → JMM 要求“所有修改对后续获取该锁的线程可见”,JVM 插入完整屏障序列 → 清空 Store Buffer + 等待 Invalidation Queue 处理完毕 + 广播 Invalidate
- final 字段初始化完成 → JMM 保证构造器内对 final 字段的写,在对象引用发布后对其他线程可见 → JVM 在构造器末尾插入 StoreStore 屏障 → 防止该写被重排到发布之后,确保 MESI 有机会同步最新值
MESI 是守门人,但不是万能裁判
MESI 保证的是缓存行状态的一致性,不是执行顺序的一致性。它只响应总线上的读写信号,不关心代码逻辑是否合理。真正导致“看似写完却读不到”的,是 CPU 为提速加的 Store Buffer 和 Invalidation Queue:
- Core A 执行 x = 1 后,值先落在 Store Buffer 中,尚未写入 L1 缓存 → MESI 状态仍是 S 或 E,不会广播 RFO → Core B 读 x 仍看到旧值
- Core B 收到 Core A 的 Invalidate 请求,但该请求暂存在 Invalidation Queue 中未处理 → 此时它读 x,仍从缓存返回旧值
- JMM 用 volatile 修饰 x,就是在写 x 前后插入屏障:写前清 Store Buffer(x86 lock 指令)、读后等 Invalidation Queue 完成(load-acquire 语义),把这两处“延迟”堵住
不同 CPU 架构下,JMM 和 MESI 的配合方式不同
JMM 是统一规范,但底层实现因硬件而异。x86 和 ARM 对同一 JMM 语义的转译策略差异明显:
- x86 是强内存模型:普通写天然带 StoreLoad 效果,Store Buffer 刷新快,Invalidation Queue 处理及时 → 无同步的 volatile 问题极难复现
- ARM/AArch64 是弱内存模型:普通写不隐含屏障,Store Buffer 和 Invalidation Queue 延迟更显著 → 同样一段代码,在 ARM 上更容易观察到 x == 1 && y == 0 这类乱序现象
- volatile 在 x86 编译为 lock 前缀指令,在 ARMv8+ 编译为 stlr(store-release),二者都强制刷新 Store Buffer 并等待失效完成,从而在各自平台上兑现 JMM 的可见性承诺
JMM 屏障不是凭空加的,而是精准干预 MESI 流程的关键点
内存屏障不是让 CPU “慢下来”,而是让它在特定位置做一次“状态确认”:
- StoreLoad 屏障(volatile 写后):等本核 Store Buffer 清空 + 所有核的 Invalidation Queue 处理完 → 确保后续读一定能见到最新值
- LoadLoad 屏障(volatile 读后):阻止后续普通读重排到该读之前 → 防止读到尚未被 MESI 同步的旧缓存副本
- MonitorEnter/Exit 对应的全屏障:不只是刷变量,还要求 MESI 完成一次完整的 M→I→S 状态流转,确保临界区内外的内存操作边界清晰
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











