Java 内存模型(Java Memory Model)

一、概述

Java 内存模型(简称 JMM):定义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式。
其和内存区域是不一样的东西。内存区域是指 JVM 运行时将数据分区域存储,强调对内存空间的划分,即运行时数据区(Runtime Data Area)。

二、Java 内存模型

JMM 是共享内存的并发模型,线程之间主要通过读-写共享变量(堆内存中的实例域,静态域和数组元素)来完成隐式通信。JMM 控制 Java 线程之间的通信,决定一个线程对共享变量的写入何时对另一个线程可见。

三、计算机高速缓存和缓存一致性

计算机在高速的 CPU 和相对低速的存储设备之间使用高速缓存,作为内存和处理器之间的缓冲。将运算需要使用到的数据复制到缓存中,让运算能快速运行,当运算结束后再从缓存同步回内存之中。

在多处理器的系统中(或者单处理器多核的系统),每个处理器内核都有自己的高速缓存,它们共享同一主内存(Main Memory)。当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致。

为此,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议进行操作,来维护缓存的一致性。Java 内存模型(Java Memory Model)_第1张图片

四、JVM 主内存与工作内存

Java 内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量(线程共享的变量)存储到内存从内存中取出变量这样底层细节。

Java 内存模型中规定了所有的变量都存储在主内存中,每条线程有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。

这里的工作内存是 JMM 的一个抽象概念,也叫本地内存,其存储了该线程读/写共享变量的副本。就像每个处理器内核拥有私有的高速缓存,JMM 中每个线程拥有私有的本地内存。

不同线程之间无法直接访问对方工作内存中的变量,线程间的通信一般有两种方式进行,一是通过消息传递,二是共享内存。Java 线程间的通信采用的是共享内存方式,线程、主内存和工作内存的交互关系如图:Java 内存模型(Java Memory Model)_第2张图片

这里所讲的主内存、工作内存与 Java 内存区域中的 Java 堆、栈、方法区等并不是同一个层次的内存划分,这两者基本上是没有关系的,如果两者一定要勉强对应起来,那从变量、主内存、工作内存的定义来看,主内存主要对应于 Java 堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域。

五、重排序和 happens-before 规则

在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三种类型:

  1. 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
  2. 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism,ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  3. 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

从 Java 源代码到最终实际执行的指令序列,会分别经历下面三种重排序:Java 内存模型(Java Memory Model)_第3张图片

JMM 属于语言级的内存模型,它确保在不同的编译器和不同的处理器平台之上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。

Java 编译器禁止处理器重排序是通过在生成指令序列的适当位置会插入内存屏障(重排序时不能把后面的指令重排序到内存屏障之前的位置)指令来实现的。

happens-before

从JDK5开始,Java 内存模型提出了 happens-before 的概念,通过这个概念来阐述操作之间的内存可见性。

如果一个操作执行的结果需要对另一个操作可见这里的“可见”是指当一条线程修改了某个变量的值,新值对于其他线程来说是可以立即得知的,那么这两个操作之间必须存在 happens-before 关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。

如果甲 happens-before 乙,那么 Java 内存模型将向程序员保证甲操作的结果将对乙可见,且甲的执行顺序排在乙之前。

重要的 happens-before 规则如下:

  1. 程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。
  2. 监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。
  3. volatile变量规则:对一个 volatile 域的写,happens- before 于任意后续对这个 volatile 域的读。
  4. 传递性:如果甲 happens- before 乙,且乙 happens- before 丙,那么甲 happens- before 丙。

如图是 happens-before 与 JMM 的关系:Java 内存模型(Java Memory Model)_第4张图片

你可能感兴趣的:(JVM,java)