一、为什么要有内存模型
- 在现代多核处理器中,每个处理器都有自己的缓存,需要定期的与主内存进行协调。
- 想要确保每个处理器在任意时刻知道其他处理器正在进行的工作,将需要很大的开销,且通常是没必要的。
1.1 硬件的效率与一致性
1、 由于计算机的存储设备与处理器的运算能力之间有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中没这样处理器就无需等待缓慢的内存读写了。
2、多个处理器运算任务都涉及同一块主存,需要一种协议可以保障数据的一致性,这类协议有MSI、MESI、MOSI及Dragon Protocol等。Java虚拟机内存模型中定义的内存访问操作与硬件的缓存访问操作是具有可比性的。
3、基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,但是引入了一个新的问题:缓存一致性(Cache Coherence)。在多处理器系统中,每个处理器都有自己的高速缓存,而他们又共享同一主存,下面会介绍这个问题
二、CPU和缓存一致性
2.1 为什么需要CPU cache
因为CPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存,浪费资源。CPU往往需要重复处理相同的数据、重复执行相同的指令,如果这部分数据、指令CPU能在CPU缓存中找到,CPU就不需要从内存或硬盘中再读取数据、指令,从而减少了整机的响应时间,所以cache的出现,是为了缓解CPU和内存之间速度的不匹配问题(结构:cpu -> cache -> memory)
在程序执行的过程中就变成了:
当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU
进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高
速缓存中的数据刷新到主存当中。
在Intel官网上产品-处理器界面内对缓存的定义为:CPU高速缓存是处理器上的一个快速记忆区域。英特尔智能高速缓存(SmartCache)是指可让所有内核动态共享最后一级高速缓存的架构。这里就提及到了最后一级高速缓存的概念,即为CPU缓存中的L3(三级缓存),那么我们继续来解释一下什么叫三级缓存,分别又是指哪三级缓存。
2.2 三级缓存(L1、L2、L3)
三级缓存(L1一级缓存、L2二级缓存、L3三级缓存)都是集成在CPU内的缓存
它们的作用都是作为CPU与主内存之间的高速数据缓冲区
L1最靠近CPU核心,L2其次,L3再次
运行速度方面:L1最快、L2次快、L3最慢
容量大小方面:L1最小、L2较大、L3最大
CPU会先在最快的L1中寻找需要的数据,找不到再去找次快的L2,还找不到再去找L3,L3都没有那就只能去内存找了。
单核CPU只含有一套L1,L2,L3缓存;如果CPU含有多个核心,即多核CPU,则每个核心都含有一套L1(甚至和L2)缓存,而共享L3(或者和L2)缓存。
单CPU双核的缓存结构:
在单线程环境下,cpu核心的缓存只被一个线程访问。缓存独占,不会出现访问冲突等问题
在多线程场景下,在CPU和主存之间增加缓存,就可能存在缓存一致性问题,也就是说,在多核CPU中,每个核的自己的缓存中,关于同一个数据的缓存内容可能不一致,这也就是我们上面提到的缓存一致性的问题
2.3 乱序执行优化
从java源码到最终实际执行的指令序列,会经历下面3种重排序:
重排序的现象:
a=10,b=a 这一组 b依赖a,不会重排序
a=10,b=50 这一组 a和b 没有关系,那么就有可能被重排序执行 b=50,a=10
cpu和编译器为了提高程序的执行效率会按照一定的规则允许指令优化,不影响单线程程序执行结果,但是多线程就会影响程序结果