1、计算机物理内存结构(物理内存结构决定了虚拟机内存结构)
由于处理器和主存储设备在运算速度上不上同一个数量级上,因此处理器和主存储器之间不得不增设一层高速缓存,将部分在主存储设备的运算放在高速缓存内,从而使得内存的相关运算速度跟上处理器的运算速度不至
于限制处理器的处理速度。实际工作中,处理器要操作的数据先从主存储内存内读取到高速缓存内,然后再在高速缓存内进行运算,最后再把运算的结果会写到主存储内存内。
2、虚拟机内存模型屏蔽不同物理机器的内存模型
不同的物理机器存在不同的架构,在内存处理方式上不一样,虚拟机内存模型就是屏蔽底层硬件、操作系统在内存操作上的的差异性,使得Java程序内存的访问方式具有跨平台的特性不限定于特定的底层内存访问方式。
也就是说Java虚拟机的内存模型是对底层硬件和操作系统对内存操作方式(读写访问)的抽象。(虚拟机的内存模型是对物理机内存模型虚拟)。
虚拟机内存模型根据物理机内存模型的抽象出的模型中,线程从共享内存(主内存)中读取所需要的数据,并加载到当前线程私有的内存区域(工作内存)进行运算并将运算的结果写入当前的私有线程当中,最后在将数
据同步至共享内存当中。也就是说明了为了执行线程和获取所需的数据,数据需要在线程私有的内存和共享的内存之间进行不断的交互操作。相关线程要读取数据,需要将共享内存(主内存)当中加数据加载到线程自己的私
有内存当中进行,相关线程执行业务操作需要保存数据就需要将线程私有的内存保存至主内存内。
3、缓存一致性问题
数据在私有内存和共享内存之间进行不断的交互操作,在并发的情况下,易造成数据的不一致性(共享内存和私有内存之间的缓存数据的不一致性),因而内存之间的交互操作要遵循相应的协议,内存间的交互要根据协
议进行操作。
4、Java虚拟机内存模型
(1)主内存和工作内存
主内存:主内存是公有,各个线程共享的内存,Java内存模型规定了所有变量都存储在主内存当中,对应于虚拟机内存区域内的Java堆中的对象实例部分,可以说是对物理主内存的抽象和表示。
工作内存:线程私有的, 保存了对主内存部分变量的副本,是对主内存的拷贝。对应于虚拟机内存区域内的虚拟机栈的部分区域,可以说是对物理高速缓存或者寄存器的抽象。
(2)内存间交互操作
一个变量如何从主内存拷贝至工作内存当中?又如何将工作内存的数据同步至主内存当中?主内存和工作内存之间是如何交互的?
Java虚拟机通过8个原子操作来实现主内存和工作内存之间的交互操作
内存间交互操作的准则:
(3)内存模型对volatile变量定义的特殊规则
volatile关键字:Java虚拟机提供的轻量级同步机制
volatile的两个特性:
a.volatile变量保证对所有线程的可见性(其他线程读取volatile变量的时候必须先从主内存加载到工作内存使用),final关键字和synchronized关键字也可以实现可见性
b.禁止指令重排序优化(插入内存屏障来保证处理器不发生乱序执行)
volatile在并发下是不安全的,保证volatile并发下安全的两条规则:
a.运算结果不依赖当前变量的值或者只有单一的线程修改变量值
b.变量不需要和其他状态变量共同参与不变约束
Java内存模型中对volatile变量定义的特殊规则:
a.线程对volatile变量的use操作和volatile变量的load、read动作相关联,必须连续一起出现(load_read_use) => volatile变量可见性的保证,保证所有线程从主内存内加载的最新的值
b.线程对volatile变量的assign操作和store、write动作相关联,必须连续一起出现 (assign_store_write) => volatile变量可见性的保证,将当强最新的值同步至主内存,保证其他线程对最新纸的可见性
c.如果线程A对volatile变量的use或assign操作先于其他线程的use或assign则,线程A对该线程的load或者store操作必定先于其他线程的load或者store操作 =>禁止指令的重排序优化
(4)long和double的非原子性协定
虚拟机允许不保证64位数据类型的load、store、 read、write 4个操作的原子性,但是商用虚拟机都选择把64位数据的读写操作作为原子操作。
(5)内存模型的3个特性
a.原子性:虚拟机模型通过内存间原子性的交互操作来保证内存的访问操作即使在并发操作仍是安全的
b.可见性:规定相关原子性操作的组合来实现变量值在所有线程间的可见性
c.有序性:通过禁止指令重排序优化,来达到代码执行的有序性
( 6)先行发生原则:判断数据是否存在竞争、线程是否安全的主要依据
对于先行发生的操作,虚拟机不进行重排序。
Java内存模型内默认不重排序的情况(天然的先天发生):
a、程序次序规则:程序代码本身的次序的先天发生,代码前面的操作先于后面代码的操作
b.管程锁定规则:同一个锁的lock操作必定先于unlock操作
c.volatile变量规则:volatile变量的写操作必定先于该变量的读操作(volatile变量的可见性保证)
d.线程启动规则:线程的启动方法start()必定先于线程的其他任何操作
e.线程终止规则:线程的终止检测必定晚于线程的任何操作
f.线程中断规则:线程的interrupt中断方法必定先去线程中断事件检测方法的
g.对象终结规则:对象的初始化操作必定先于对象的finalize()
h.传递性规则:线程先行发生的传递性