为什么要有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)
Java内存模型(JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实体字段,静态字段和构成数组对象的)的访问方式。
由于JVM运行程序的实体是线程,而每个线程创建时JVM会为其创建一个工作内存(栈空间),用于存储线程私有数据,而Java内存模型中规定所有变量都存储在主内存,主内存时共享内存区域,所有线程都可以访问,但线程如果想要对一个变量读取赋值等操作那么必须在工作内存中进行,所以线程想要操作变量时首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量刷写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝。
JMM描述的是一组规则,围绕原子性、有序性和可见性展开
主内存:主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中(除了开启逃逸分析和标量替换的栈上分配和TLAB分配),不管该实例对象时成员变量还是局部变量,当然也包括了共享的类信息、常量、静态变量。由于是共享数据区域,多条线程对同一个变量进行非原子性操作时可能会发生线程安全问题。
工作内存:主要存储当前方法的所有本地变量信息(工作内存中存储着主内存中的变量副本拷贝),每个线程只能访问自己的工作内存,即线程中的本地变量对其他线程时不可见的,就算是两个线程执行的同一段代码,它们也会各自在自己的工作内存中创建属于当前线程的本地变量。由于工作内存是每个线程的私有数据,线程间无法访问工作内存,线程之间的通讯还需要依赖与主存,因此存储在工作内存的数据不存在线程安全问题。
四:JVM内存模型和JMM内存模型
JVM内存模型是出于Java的JVM虚拟机层面的,实际上对于操作系统来说,本质上JVM还是存在于主存中,而JMM是Java语言与OS和硬件架构层面的,主要作用是规定硬件架构与Java语言的内存模型,而本质上不存在JMM这个东西,JMM只是一种规范,并不能说是某些技术实现。