目录
Java内存结构
JDK1.8之前
JDK1.8之后(hotpot虚拟机的实现)
直接内存
Java 内存模型
指令重排序
JMM 抽象线程和主内存之间的关系
编辑
Happens-Before规则
参考文献
JVM的内存结构和内存模型是两个很容易混淆的概念,然而这两个术语的内涵却天差地别。
内存结构是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分。
而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之间的抽象关系,是在底层处理器内存模型的基础上(确保了正确同步的Java 代码可以在不同体系结构的处理器上正确运行。),定义自己的多线程语义。它明确指定了一组排序规则,来保证线程间的可见性。一句话概述:Java 内存模型描述的是多线程对共享内存修改后彼此之间的可见性。
(图片来源于JavaGuide)
jvm将虚拟机分为5大区域,程序计数器、虚拟机栈、本地方法栈、java堆、方法区;
程序计数器主要有两个作用:
注意:
1.Java 虚拟机规范对于运行时数据区域的规定是相当宽松的。以堆为例:堆可以是连续空间,也可以不连续。堆的大小可以固定,也可以在运行时按需扩展 。虚拟机实现者可以使用任何垃圾回收算法管理堆,甚至完全不进行垃圾收集也是可以的。
2.JDK 1.7 将字符串常量池移动到堆中,原因是:永久代(方法区实现)的 GC 回收效率太低,只有在整堆收集 (Full GC)的时候才会被执行 GC。Java 程序中通常会有大量的被创建的字符串等待回收,将字符串常量池放到堆中,能够更高效及时地回收字符串内存。
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。这是HotPot虚拟机的实现
运行时常量池、方法区、字符串常量池这些都是不随虚拟机实现而改变的逻辑概念,是公共且抽象的,Metaspace、Heap 是与具体某种虚拟机实现相关的物理概念,是私有且具体的。
要想真正理解JMM,首先先来看看指令重排序问题
为了提升执行速度/性能,计算机在执行程序代码的时候,会对指令进行重排序。常见的指令重排序有下面 2 种情况:
1.编译器优化重排 :编译器(包括 JVM、JIT 编译器等)在不改变单线程程序语义的前提下,重新安排语句的执行顺序。
2.指令并行重排 :现代处理器采用了指令级并行技术(Instruction-Level Parallelism,ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
Java 源代码会经历 编译器优化重排 —> 指令并行重排 —> 内存系统重排 的过程,最终才变成操作系统可执行的指令序列。
指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致 ,所以在多线程下,指令重排序可能会导致一些问题。JMM就是为了解决多线程访问共享变量时的同步问题的
对于 Java 开发者说,你不需要了解底层原理,直接使用并发相关的一些关键字和类(比如 volatile
、synchronized
、各种 Lock
)即可开发出并发安全的程序。
在当前的 Java 内存模型下,线程可以把变量保存 本地内存 (比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。
从上图来看,线程 A与线程 B 之间如果要进行通信的话,必须要经历下面 2 个步骤:
也就是说,JMM 为共享变量提供了可见性的保障。
不过,多线程下,对主内存中的一个共享变量进行操作有可能诱发线程安全问题。举个例子:
为了线程间的可见性,JMM定义了 Happens-Before规则, JMM 规定,要想保证 B 操作能够看到 A 操作的结果(无论它们是否在同一个线程),那么 A 和 B 之间必须满足 Happens-Before 关系:
1.单线程规则:一个线程中的每个动作都 happens-before 该线程中后续的每个动作
2.监视器锁定规则:监听器的解锁动作 happens-before 后续对这个监听器的锁定动作
3.volatile 变量规则:对 volatile 字段的写入动作 happens-before 后续对这个字段的每个读取动作
4.线程 start 规则:线程 start() 方法的执行 happens-before 一个启动线程内的任意动作
5.线程 join 规则:一个线程内的所有动作 happens-before 任意其他线程在该线程 join() 成功返回之前
6.传递性:如果 A happens-before B, 且 B happens-before C, 那么 A happens-before C