内存可见性出现的原因

126 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情 在X86架构下CPU缓存的布局是:一个CPU有三级缓存,而CPU的缓存与主内存之间是通过总线进行交互的,所以缓存和主内存之间存在不同步的问题,这个不同步问题是通过MESI协议解决的。

但是MESI协议对性能有很大损耗,为了解决这个问题,在CPU计算单元和L1缓存中加了store buffer和load buffer,提升性能。

所以在X86架构下由于MESI协议,使得主内存和L1、L2和L3实现同步,但是store buffer、load buffer和L1之间是异步的,也就是说往内存中写入一个变量,这个变量会保存在store buffer里面,稍后异步写入到L1中,同时同步写入主内存中。

往上到操作系统内核视角,CPU缓存模型就是多个逻辑CPU都有自己的缓存,缓存和主内存之间是不同步的

在往上到JVM层面,工作线程有自己的本地内存,本地内存和共享内存之间存在不同步问题。

这就是CPU内存重排序

还有CPU指令重排序和编译器重排序

为了禁止编译器重排序和CPU重排序,在编译器和CPU层面都有对应的指令,也就是内存屏障,这也就是JMM和happens-before原则的底层实现原理

其中编译器的内存屏障,就是告诉编译器不要对指令进行重排序,当编译完成之后,这种内存屏障就会消失,CPU并不会感知到编译器中内存屏障的存在

而CPU的内存屏障是CPU提供的指令,可以由开发者显式调用

由于在X86架构下,只会产生StoreLoad乱序,也就是读操作在写操作之前执行

JVM层面采用StoreLoad内存屏障,采用fence方法来实现,而X86平台存在lfence、sfence和mfence来保证loadload屏障、storestore屏障和全屏障,mfence能满足storeload内存屏障,但是不lock指令性能更高一些,所以最终采用的是lock指令来实现内存屏障