JVM
Java虚拟机(Java Vitural Machine),简称JVM.JVM是安装在操作系统上的,它的初衷是为了屏蔽操作系统与计算机硬件之间的差异,Java为不同的系统提供不同的JVM,所以才能保证Java能一处编译到处运行,JVM的本质也是一款软件.
JVM的内存结构如下:
这里只要了解一下的组成部分即可,后续再深入讲解.
JMM
提到JMM,很多人第一反应可能就是上面的那张图了,其实他们两者是没有任何关系的.JMM全称是Java Memroy Model,也就是Java内存模型,它是一个理论模型,并不是真实存在的.JMM的目的就是有用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果.
在讲解JMM之前不得不讲一下当代计算的一个内存模型,我们知道当代计算机处理能力是非常快的,而这些都得益于运算超快的处理器,但是觉大多数的运算任务都不可能只靠处理器”计算”就能完成,处理器至少要与内存交互,如读取数据,存储运算结果等,这个I/O操作时很难消除的(无法仅靠寄存器来完成所有运算任务).由于计算的存储设备与处理器的运算速度有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度地高速缓存(Cache)来作为内存与处理器之间地缓冲:将运算需要使用到的数据复制到缓存中,让运算能快速及逆行,当运算结束候再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了.
Java内存模型也是与此类似的.
Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件是的主内存名字一样,两者也可以相互类比,但此处仅仅是虚拟机内存的一部分).每条线程都还有自己的工作内存(Working Memory,可与上面的处理器告诉缓存类比),线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对该变量的所有操作(读取,赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量,线程间变量值的传递均需要通过主内存来完成.
这里所讲的主内存,工作内存与Java内存区域中的堆,栈,方法去等并不是同一个层次的内存划分,这两者基本上是没有关系的,如果两者一定要勉强对应起来,那从变量,主内存,工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域.从更低层次来说,主内存就是对应于物理硬件的内存,而为了获取更好的运行速度,虚拟机(甚至是硬件系统本身的优化措施)可能回让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问读写的是工作内存.
八大操作:
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
- assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
- store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
- write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
JMM对这八种指令的使用,制定了如下规则:
- 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
- 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存 (可见)
- 不允许一个线程将没有assign的数据从工作内存同步回主内存
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
- 对一个变量进行unlock操作之前,必须把此变量同步回主内存