在计算机系统的发展过程当中,因为CPU的运算速度和计算机存储速度之间巨大的差距。为了解决CPU的运算速度和计算机存储速度之间巨大的差距,设计人员在CPU和计算机存储之间加入了高速缓存来作为他们之间的桥梁,在运算时,先将数据拷贝到高速缓存中,计算完成后再将结果写入计算机存储,这样大大提升了计算效率,避免重复屡次访问计算机存储形成的cpu资源浪费。html
尽管这样,CPU仍是存在不少空闲的时间段,为了压榨CPU的性能,多任务处理诞生了,同时多任务处理致使任务之间共享资源的争抢,从而引起了并发问题。java
在Java应用程序中,为了更好的解决并发问题,就必须深刻理解Java内存模型。Java内存模型是Java虚拟机很是重要的一部分,它用来指导Java虚拟机是如何与计算机硬件内存之间协同工做的。在了解Java内存模型以前,咱们先来看看计算机硬件内存模型是怎么样的。编程
同时每一个CPU之上还存在高速缓存,但高速缓存的层级和位置是不固定的,现代计算机的缓存层级不少达到了三级,将来可能更多。缓存的位置也各有不一样,有的集成了部分缓存到CPU中。数组
一样,缓存的读写速度也大大快于计算机主存储器,在寄存器和主存储器之间,这样的CPU--缓存--主存储的三层结构就构成了硬件内存模型。缓存
CPU在程序的执行过程当中,常常会频繁的调用相同的数据,好比在一个循环内调用了位于另一个物理地址的函数,这个函数可能与当前指令的物理位置相距甚远,由于程序使用的物理内存并非连续的,这就致使了须要花费不少没必要要的时间在物理寻址上。但若是在CPU计算以前会将所须要用到的数据先读到缓存中,计算完成以后再一次性写入计算机主存储器,就能够避免频繁访问计算机主存储器形成的资源浪费。多线程
上面说了计算机的硬件内存模型,Java内存模型和硬件内存模型有不少相似的地方。因为存在不一样的计算机操做系统类型和硬件类型,致使各类平台下物理内存模型的不一致。为了让Java上层开发有一个统一的内存访问操做,保证多线程对共享数据的读写一致性,JVM规范定义了Java内存模型(Java Memory Model JMM)。 并发
JMM还规定了全部的变量都存储在主内存中(MainMemory),同时每一个线程有本身的本地内存(LocalMeory,也叫工做内存),本地内存中保存了所须要用到的主内存数据的拷贝。线程对变量的读和写都在本地内存中进行。app
是否是发现JMM和硬件内存模型存在不少类似之处?主内存对应计算机主存储,本地内存对应高速缓存。但要知道它们虽然能够类比,却并非相同的东西。函数
本地内存仅仅是JMM的一个抽象概念,实际上JVM中并不存在这样一个区域来对应,这个区域在广义上能够包括缓存、寄存器以及其余的硬件和编译器优化等等。这句话可能听起来比较难懂,咱们只须要知道线程对共享变量的操做并不会直接访问主内存,而是访问一个中间层,这个中间层包含了主内存中变量的拷贝,同时中间层的访问速度大大快于访问主内存的速度,在必定的操做以后将结果统一写回主内存,这样就大大提升了程序的性能。性能
同时也会产生另一个问题,同一个共享变量在每个线程之中都会有一份拷贝(对引用类型,并非拷贝所有数据),产生的线程越多,缓存开销也就越大。
JVM内存模型定义的是线程堆栈和堆之间的内存划分,它和Java内存模型是有区别的,参照《深刻理解Java虚拟机》中的解释:
这二者本没有关系。若是必定要勉强对应,那从变量、主内存、工做内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分,而工做内存则对应于虚拟机栈中的部分区域。从更低层次上说,主内存就是物理内存,而为了获取更好的执行速度,虚拟机(甚至是硬件系统自己的优化措施)可能会让工做内存优先存储于寄存器和高速缓存中,由于运行时主要访问——读写的是工做内存。
全部的原始类型(boolean,byte,short,char,int,long,float,double)局部变量都存储在线程堆栈中,不对其余线程共享。堆中则包含了Java程序中建立的对象。 举个例子:
public class MemoryModel {
public int i = 0;
public void methodOne() {
int localVarOne = 1;
SharedObject localVarTwo = SharedObject.sharedObject;
Integer localVarThree = new Integer(1);
}
}
public class SharedObject {
pubic static SharedObject sharedObject = new SharedObject();
public int sharedVarOne = 1;
}
复制代码
代码中局部变量localVarOne存储在线程堆栈中。局部变量localVarTwo的引用存储在线程堆栈中,但对象自己存储在堆上。局部变量localVarThree同localVarTwo同样,引用存储在线程堆栈中,但对象自己存储在堆上。不一样的是多线程执行methodOne方法时,localVarTwo因为是静态类型,在堆中只有一份数据,而localVarThree在堆和堆栈中都有多份数据。局部变量对象的成员变量sharedVarOne也存储在堆上,不管sharedVarOne是基本类型仍是引用类型都是如此。
参考资料: