主要区别,java内存结构是对内存的具体划分,java内存模型是解决多线程下工作线程和主线程数据不一致问题而提出的抽象规则。
java内存结构简单划分如下图所示,以下结构是java8之前版本,在java8里元空间取代了之前版本的方法区
可以看到java内存主要划分线程共享(堆、方法区),线程私有(程序计数器、虚拟机栈、本地方法栈),以下就这几个内存区域进行说明
程序计数器
- 当前线程所执行的字节码的行号指示器
- 多线程下用来记录线程切换后当前线程的执行位置
- 唯一不会出现OOM的区域
Java虚拟机栈
- 栈描述的是Java方法执行的内存模型
- 栈是由一个个栈帧组成,每个栈帧都拥有:局部变量表、操作数栈、动态链接、方法出口信息
- 每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程
- 局部变量表存放编译器可知的数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)
- 可能会出现StackOverFlowError和OutOfMemoryError两种异常
本地方法栈
Java虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈为虚拟机使用Native方法服务,可能底层调用的c或者c
堆
Java虚拟机管理内存最大的一块内存区域,虚拟机启动时创建,唯一目的存放对象实例,几乎所有对象实例及数组都在这里分配,因为JIT编译器发展和逃逸分析技术的成熟导致并不是所有对象都在堆中
- 即使编译器:可以把Java字节码包括需要被解释的指令程序转换成可以直接发送给处理器的指令的程序
- 如果开启了逃逸分析,可将某些实例或者变量直接在栈上分配
方法区
- 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,逻辑上属于堆的一部分,但确有个Non-Heap(非堆)的别名,目的为了区分堆
- HotSpot虚拟机方法区也被称为永久代,但本质不等价,仅因为用永久代来实现方法区而已
- 垃圾回收不会发生在永久代,如果永久代满了或超过临界值,会触发完全垃圾回收(Full GC),这就是为什么设置正确的永久代大小对避免Full GC是非常重要的
- JDK1.8方法区被彻底移除(JDK1.7就已经开始),取代的是元空间(直接使用的本地内存),原因是很难确定永久代的大小,依赖很多因素,比如JVM加载的class的总数、常量池的大小等,不确定永久代大小很容易导致其OutOfMemoryError异常
运行时常量池
- 运行时常量池是方法区的一部分,class文件除了类的版本、字段、接口、方法等描述信息外,还有常量池(存放编译生成的各种字面量和符号引用),常量池会在类被加载到运行时数据区时存放到运行时常量池
- JDK1.7将字符串常量池从运行时常量池移到堆中
直接内存
- 不是运行时数据区的一部分,但这部分内存会被频繁地使用,也可能导致OutOfMemoryError异常
- JDK1.4引入的NIO,直接使用Native本地函数库操作直接内存,通过存储在堆中的DirectByteBuffer对象作为这块内存的引用,避免在Java堆中和直接内存来回复制数据,实现零拷贝,显著提高性能