为什么要有java内存模型?
一:背景
1.现有计算机往往是多核的,每个外围下会有高速缓存。高速缓存的诞生是因为[CPU与内存(主存)的速度存在差别](因为CPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存。CPU往往需要重复处理相同的数据、重复执行相同的指令,如果这部分数据、指令CPU能在CPU缓存中找到,CPU就不需要从内存或硬盘中再读取数据、指令,从而减少了整机的响应时间,L1和L2缓存个别是[每个外围独占]一份的。
2.为了让CPU进步运算效率,处理器可能会对输出的代码进行[乱序执行]。也就是所谓的[指令重排序]
3.一次对数值的批改操作往往是非原子性的(比方i++实际上在计算机上执行时就会分成多个执行)
4.在永远单线程下,下面所讲的均不会存在什么问题,因为单线程意味着无并发。并且在单线程下,编译器/runtime/处理器都必须恪守as-if-serial语义,恪守as-if-serial意味着他们不会对[数据依赖关系的操作]做重排序
5.CPU为了效率,有个高速缓存、有了指令重排序等等,整块架构都变得复杂了。要想充分利用CPU资源,应用起了多线程。
6.多线程意味着并发,并发就意味着咱们要思考线程平安问题
7.缓存数据不统一:多个线程同时批改[共享变量],CPU外围下的高速缓存是[不共享]的,那多个cache与内存之间的数据同步该怎么做?
8.CPU指令重排序在多线程下会导致代码在非预期下执行,最终会导致后果存在谬误的状况
9.针对于[缓存不统一]问题,CPU也有其解决办法,常被大家所意识的有两种:
1.应用[总线锁]:某个外围在批改数据的过程中,其余外围均无法批改内存中的数据(相当于独占内存的概念,只有有CPU在批改,那别的CP就得期待以后CPU开释)
2.缓存一致性协议
10.缓存一致性协议我认为能够了解为[缓存锁],它针对的是[缓存行](Cache line)进行“加锁”,所谓[缓存行]其实就是高速缓存存储的最小单位
二:CPU和缓存一致性
三级缓存(L1、L2、L3)
1.三级缓存(L1一级缓存、L2二级缓存、L3三级缓存)都是集成在CPU内得缓存
2.它们的作用都是作为CPU与主内存之间的高速数据缓冲区
3.L1最靠近CPU核心,L2其次,L3再次 运行速度方面:L1>L2>L3 ,容量L3>L2>L1
4.CPU会先在最快的L1中寻找需要的数据,找不到再去找次快的L2,还找不到再去找L3,L3都没有那只能去内存找了
5.单核CPU只含一套L1,L2,L3缓存;如果CPU含有多个核心,即多核CPU,则每个核心都含有一套L1(甚至和L2)缓存,而共享L3(或者和L2)缓存
单CPU双核的缓存结构
在多核多线程环境下,对于同一份数据不同cpu处理器的缓存数据可能出现不一致
乱序执行优化
从java源码到最终实际执行的指令序列,会经历下面3种重排序
重排序的现象:
- a=10,b=a 这一组 b 依赖a,不会重排序
- a=10,b=50 这一组 a=b没有关系,那么就有可能被重排序执行b=50,a=10 cpu和编译器为了提高程序的执行效率会按照一定的规则允许指令优化,不影响单线程程序执行结果,但是多线程就会影响程序结果
三:java内存模型(java Memory Model,简称JMM)
JMM定义了Java虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的
JMM屏蔽了各种硬件和操作系统的内存访问差异,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。可以避免像c++等直接使用物理硬件和操作系统的内存模型在不同操作系统和硬件平台下表现不同,比如有些c/c++程序可能在windows平台运行正常,而在linux平台却运行有问题。
Java线程之间的通信采用的是共享内存模型(JMM),JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:
线程之间的共享变量存储在主内存(main memeory)中
每个线程都有一个私有的本地内存(local memeory)
本地内存中储存了该线程以读/写共享变量的副本。
本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区、寄存器以及其他的硬件和编译器优化
JMM描述的是一组规则,围绕原子性、有序性和可见性展开
参考: Java内存模型(JMM)详解 - 知乎
关于java:面试官为什么需要Java内存模型 - 乐趣区