Java内存模型(Java Memory Mode,JMM)
Java的内存模型指定了Java虚拟机如何与计算机的主存(RAM)进行工作,如上图所示,理解Java内存模型对于编写行为正确的并发程序是非常重要的。在JDK1.5以前的版本中,Java内存模型存在着一定的缺陷,在JDK1.5的时候,JDK官方对Java内存模型重新进行了修订,在JDK1.8及最新的JDK版本都沿用了JDK1.5修订的内存模型。
Java的内存模型决定了一个线程对共享变量的写入何时对其他线程可见,Java内存模型定义了线程和主内存之间的抽象关系,具体如下:
- 共享变量存储于主内存之中,每个线程都可以访问。
- 每个线程都有私有的工作内存或者称为本地内存。
- 工作内存只存储该线程对共享变量的副本。
- 线程不能直接操作主内存,只有先操作本地内存之后才能写入主内存。
-
工作内存和Java内存模型一样也是一个抽象的概念,它其实并不存在,它涵盖了缓存、寄存器、编译器优化以及硬件等。
JVM内存模型
1、Class Loader(类加载器)就是将Class文件加载到内存,再说的详细一点就是,把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是类加载器的作用。
2、Run Data Area(运行时数据区) 就是我们常说的JVM管理的内存了,也是我们这里主要讨论的部分。运行数据区是整个JVM的重点。我们所有写的程序都被加载到这里,之后才开始运行。这部分也是我们这里将要讨论的重点。
数据 | 描述 |
---|---|
Program Counter Register(程序计数器) | 线程私有、指向下一条要很执行的指令 |
Java Stack(Java虚拟机栈) | 线程私有,生命周期与线程相同。描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口 |
Native Method Stack(本地方法栈) | 为虚拟机使用到的Native 方法服务 |
Heap(线程共享) | 所有的对象实例以及数组都要在堆上分配,是垃圾收集器管理的主要区域。由于现在收集器基本采用的分代收集算法,所以Java堆中还可以细分:新生代和老生代,更细致一点的有Eden空间、From Survivor空间、To Survivor空间等。 |
Method Area(方法区) | 被多个线程共享的内存区域,它主要用于存储已经被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等数据。 |
3、Execution engine(执行引擎) 是Java虚拟机最核心的组成部分之一。执行引擎用于执行指令,不同的java虚拟机内部实现中,执行引擎在执行Java代码的时候可能有解释执行(解释器执行)和编译执行(通过即时编译器产生本地代码执行,例如BEA JRockit),也有可能两者兼备。任何JVM specification实现(JDK)的核心都是Execution engine,不同的JDK例如Sun 的JDK 和IBM的JDK好坏主要就取决于他们各自实现的Execution engine的好坏。
4、Native interface 与native libraries交互,是其它编程语言交互的接口。当调用native方法的时候,就进入了一个全新的并且不再受虚拟机限制的世界,所以也很容易出现JVM无法控制的native heap OutOfMemory。
JVM和JMM之间的关系
JMM中的主内存、工作内存与JVM中的Java堆、栈、方法区等并不是同一个层次的内存划分,这两者基本上是没有关系的,如果两者一定要勉强对应起来,那从变量、主内存、工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域。从更低层次上说,主内存就直接对应于物理硬件的内存,而为了获取更好的运行速度,虚拟机(甚至是硬件系统本身的优化措施)可能会让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问读写的是工作内存。