三兄弟:JVM内存结构 VS Java内存模型 VS Java对象模型
JVM内存结构
- 共享区域
- 堆区(所有线程共享)
- 存储的全部是对象实例(通过new等指令创建的,并会被垃圾回收;数组也是保存在堆上面的,即使是基本类型的数据,也是保存在堆中的。因为在Java中,数组是对象),是内存中最大的一块。
- 堆的优势是可以在运行时动态地分配内存空间,不必事先告诉编译器。
- 方法区(所有线程共享)
- 它用于存储虚拟机已经加载的static静态变量、类信息、常量。
- 还存放永久引用,比如static People p = new People();的p引用。
- 运行时常量池是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后放到方法区的运行时常量池中。存放final修饰的。
- 堆区(所有线程共享)
- 线程独有
- 栈区(每个线程私有)
- 每个线程包含一个栈区,栈中保存基础数据类型(byte,short,int,long,float,double,boolean,char)的对象、自定义对象的引用(不是对象本身)和returnAddress(指向了一条字节码指令的地址)。
- 每个方法从被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。每个栈中的数据(基本类型的局部变量、参数、对象引用)都是私有的,其他栈不能访问。
- 在编译期间就确定了大小,运行期间不会改变大小。
- 本地方法栈(每个线程私有)
- 与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。
- 程序计数器(每个线程私有)
- 是最小的一块内存区域,它的作用是当前线程所执行的字节码的行号指示器,在虚拟机的模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。
- 栈区(每个线程私有)
Java对象模型
- Java对象自身的存储模型。
- 每个对象的实例保存在堆中,对象的引用保存在栈中。
- 每个对象包括对象头和实例数据
JMM是什么
为什么需要JMM?
JVM实现会带来不同的“翻译”,不同的CPU平台的机器指令又千差万别,无法保证并发安全的效果一致
- 是一组规范,需要各个JVM的实现来遵守JMM规范,以便于开发者可以利用这些规范,更方便地开发多线程程序。
- 如果没有这样的一个JMM内存模型来规范,那么很可能经过了不同JVM的不同规则的重排序之后,导致不同的虚拟机上运行的结果不一样,那是很大的问题。