JVM内存模型

一、操作系统内存模型

  由于计算机的存储设备与处理器的运算能力之间有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中没这样处理器就无需等待缓慢的内存读写了。
  基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,但是引入了一个新的问题:缓存一致性(Cache Coherence)。在多处理器系统中,每个处理器都有自己的高速缓存,而他们又共享同一主存,如下图所示:多个处理器运算任务都涉及同一块主存,需要一种协议可以保障数据的一致性,这类协议有MSI、MESI、MOSI及Dragon Protocol等。

二、java的内存模型

  C、C++等语言,都是通过直接使用物理硬件和操作系统的内存模型,会因为不同操作系统的内存模型的差异,导致无法跨平台。

  JAVA虚拟机试图定义一种java内存模型,来屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果。

  java的内存模型主要目标是定义程序中各个变量的访问规则。即虚拟机中,将变量存储到内存,和从内存中取出变量的底层细节。
  此处的变量,和java编程所说的变量,有所差别。它包括了实例字段、静态字段和构成数组对象的元素。不包括局部变量和方法参数。以为这两个是线程私有的,不会被共享,所以不会存在竞争问题。
  java内存模型规定了所有的变量都存在在主内存中。(仅仅是虚拟机内存的一部分,可以类比操作系统的主内存)。每个线程,都有自己的工作内存(保存线程使用变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中执行,而不能直接读写主内存的变量,不同的线程之间,不能访问对方的工作内存,线程间变量值,都需要通过主内存来完成)。此处的工作内存、主存和 java堆、栈、方法区并不是同一层次的概念。如果勉强对应,主存对应java堆中对象实例的数据部分。工作内存对应虚拟机栈的部分区域。更低层次,主存对应物理硬件内存,程序运行时,主要访问读写是工作内存。

内存间的交互:
lock:作用于主内存变量,将一个变量置为一个线程独占的状态。
unlock:作用于主内存变量,将一个被锁定的状态的变量释放锁, 之后,可以被其他线程锁定。
read:作用于主内存变量,将一个变量的值,从主内存传输到工作内存中,以便之后的load操作。
load:作用于工作内存变量,将read操作从主内存得到的变量放入工作内存的变量副本中。
use:作用于工作内存变量,将工作内存的一个变量值,传递给执行引擎,每当虚拟机遇到一个需要使用到变量的字节码指令时,将会执行这个操作。
assign(赋值):作用于工作内存变量,将一个从执行引擎接受到的值赋予给工作内存变量,每当虚拟机遇到一个变量赋值的字节码指令时,将会执行这个操作。
store:作用于工作内存变量,将工作内存变量传送给主内存中,以便随后的write操作。
write:作用于主内存变量,将工作内存得到的变量值放入主内存的变量中。
以上操作的规则如下
(1)不允许read和load、store和write单独出现。
即不允许一个变量从主内存读取但工作内存不接受的情况
不允许一个变量从工作内存发起写操作 ,但是主内存不接受的情况。
(2)不允许一个工作线程丢到最近的assign操作。
即一个变量在工作内存中改变了之后,必须将该改变同步到主内存。
(3)不允许一个线程无原因的将工作内存的变量数据同步到主内存中。
(4)一个新的变量只能在主内存中诞生,不允许工作内存直接使用一个未被初始化(load、assign)的变量。一个变量在use和store之前,必须先执行load和assign。
(5)一个变量同一时刻只允许同一个线程对其进行lock操作。
(6)如果对一个变量进行lock操作,将会清空所有使用该变量的线程的工作内存中此变量的值,执行引擎在使用这个变量之前,需要重新执行load和assign操作初始化该变量。
(7)如果一个变量事先没有被lock,则不允许对它进行unlock。也不允许unlock一个被其他线程锁定的变量。
(8)对一个变量执行unlock之前,需要将此变量同步到主内存中。

 

三、volatile:
对java虚拟机提供的最轻量级的同步机制。
当一个变量被volatile修饰时,它将具备两种特性:
(1)保证此变量对所有线程是可见的。
普通变量的可见性过程:修改一个普通变量,不会立刻将普通变量同步到主内存,对其他线程来说,这个修改不会立刻可见。
volatile修饰的变量,会立刻将变量同步回主内存时,其他的线程在使用该变量之前,都会重新加载此变量。
(2)禁止指令重排序优化。
指令重排序:在虚拟机层面,为了尽可能减少内存操作速度远慢于CPU运行速度所带来的CPU空置的影响,虚拟机会按照自己的一些规则(这规则后面再叙述)将程序编写顺序打乱——即写在后面的代码在时间顺序上可能会先执行,而写在前面的代码会后执行——以尽可能充分地利用CPU。


四、原子性、可见性、有序性
原子性:由java内存模型直接保证的内存原子性有read、load、assign、use、store、write。
基本数据类型的读写,基本都可以认为是原子性的。
synchronized修饰的块,也是具备原子性的。
可见性:是指当一个线程修改共享变量的值,其他线程能立刻得知这个修改。
java内存模型是通过在变量修改后,将新值同步到主内存,在变量被读取之前,从主内存刷新这个变量的值到工作内存中,这种一类主内存作为传递媒介的方法来实现可见性的。普通变量和volatile变量都是如此,他们之间不同在于:
volatile的特殊规则,保证了新值能立即同步到主内存中,以及每次其他线程使用前,都能立即从主内存刷新。
除volatile之外,sychronized和final也能实现可见性。
synchronized块的可见性,是通过“对一个变量执行unlock操作之前,必须先把变量值同步回主内存中”这个特性决定的。
final:没有把this的引用指针传递出去。
有序性:
(1)在本线程内观察,所有的操作都是有序执行的。
(2)如果一个线程观察另一个线程,则所有的操作都是无序的 。
(1)是指线程内表现为串行的语义。
(2)是指指令重排序和“工作内存和主内存同步延迟”情况。

原文地址:https://www.cnblogs.com/ironyoda/p/6289847.html