java内存模型JMM
基本概念
程序:代码,完成某一件任务,代码序列(静态的概念)
进程:程序在某些数据上的一次运行(动态的概念)
线程:一个进程可能包含一个或多个线程(占有资源的独立单元)
JVM与线程
JVM是在类被调用的时候才会被启动。先有JVM线程,然后才其他(main)线程(JVM创建了其他线程,)。
JVM 内存区域
信息共享
方法区:类信息(由类加载器加载)、常量、static、JIT (动态编译的代码)。(反射操作的地方)。
Java堆区:实例对象 GC工作主要区域 (OOM)。
数据不共享
VM stack(虚拟机栈):Java方法在运行的内存模型 (OOM)
如果虚拟机栈内存溢出指的是整体内存不足,不是单个栈帧内存不足,栈帧的内存是有大有小的。
PC(程序计数器):java线程的私有数据,这个数据就是执行下一条指令的地址
Native method stack: JVM的本地方法
java内存模型 Java memory model(JMM,抽象的概念)
主内存:共享的信息
工作内存:私有信息,基本数据类型,直接分配到工作内存,引用的地址存放在工作内存,引用的对象存放在堆中
工作方式:
A 线程修改私有数据,直接在工作空间修改
B 线程修改共享数据,把数据复制到工作空间中去,在工作空间中修改,修改完成以后,刷新内存中的数据
JMM三个特征
原子性:不可分割 (类似与银行转账,必须要一个加钱一个减钱)
A) X=10 单纯的写是有原子性。如果是私有数据具有原子性,如果是共享数据没原子性(读写)
B) Y=x 没有原子性
a) 把数据X读到工作空间(原子性)
b) 把X的值写到Y(原子性)
C) I++ 没有原子性
a) 读i到工作空间
b) +1;
c) 刷新结果到内存
D) Z=z+1 没有原子性
a) 读z到工作空间
b) +1;
c) 刷新结果到内存
多个原子性的操作合并到一起没有原子性
保证方式:
Synchronized
JUC Lock的lock
可见性:线程只能操作自己工作空间中的数据
加锁:Synchronized、Lock
关键字:Volatile:在JMM模型上实现MESI协议
有序性:程序中的顺序不一定就是执行的顺序。
(编译重排序(编译优化)、指令重排序(指令优化),为了优化代码提高效率)
as-if-aeria原则:
单线程中重排后不影响执行的结果。
Happens-before原则:
-
程序次序规则:在-一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。准确地说,应该是控制流顺序而不是程序代码顺序,因为要考虑分支。循环等结构。
-
管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。 这里必须强调的是同一个锁,而“后面"是指时间上的先后顺序。
-
volatile变量规则:对一个volatile变 量的写操作先行发生于后面对这个变量的读操作,这里的"后面"同样是指时间上的先后顺序。
-
线程启动规则: Thread对象的start()方法先行发生于此线程的每-一个动作。
-
线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread,join () 方法结束、Thread.isAlive ()的返回值等手段检测到线程已经终止执行。
-
线程中断规则:对线程interrupt()方 法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。
-
对象终结规则: -个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。
Happens-Before的1个特性:传递性。A--B--C A--C A在B前,B在C前。可以得到A的语句必须要在C语句前面执行。
Java内存模型涉及的几个关键词:锁、volatile字段、 final修 饰符与对象的安全发布。
-
锁操作是具备happens- before关系的,解锁操作happens-before之后对同一把锁的加锁操作。实际上,在解锁的时候,JVM需要强制刷新缓存,使得当前线程所修改的内存对其他线程可见。
-
volatile字段可以看 成是一-种不保证原子性的同步但保证可见性的特性,其性能往往是优于锁操作的。但是,频繁地访问volatile字段也会出现因为不断地强制刷新缓存而影响程序的性能的问题。
-
final修饰的实例字段则是涉及到新建对象的发布问题。当一个对象包含final修饰的实例字段时,其他线程能够看到已经初始化的final实例字段,这是安全的。
Java内存模型底层怎么实现的
主要是通过内存屏障(memory barrier)禁止重排序的,即时编译器根据具体的底层体系架构,将这些内存屏障替换成具体的CPU指令。对于编译器而言,内存屏障将限制它所能做的重排序优化。而对于处理器而言,内存屏障将会导致缓存的刷新操作。比如,对于volatile,编译器将在volatile字段的读写操作前后各插入一些内存屏障。