- JVM内存结构
- Java内存模型
- Java对象模型
- 总结
首先分清楚几个词:JVM内存结构、Java内存模型、Java对象模型
JVM内存结构
Java程序执行过程中,内存会被划分为不同的数据区域,各个区域有各自的用途。
有些区域随虚拟机的启动而存在。有些区域随线程的启动而启动,随线程的结束而销毁。
线程私有的内存区域:
- 程序计数器:可看做当前线程执行字节码的行号指示器,字节码解释器工作时通过改变计数器的值来选择下一条所需执行的字节码指令
- 虚拟机栈:Java方法执行的栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用至执行完成的过程,都对应一个栈帧在虚拟机栈的入栈到出栈的过程。其中局部变量表是指:存放编译期可知的基本数据类型(boolean、byte、char、int等)、对象引用(reference类型)和 returnAddress类型(指向一条字节码指令的地址)
- 本地方法栈:Native方法(非Java实现的方法)执行的栈帧
所有线程共享的内存区域:
- 堆:存放对象实例和数组,GC主要管理这一块
- 方法区:存储被虚拟机加载的Class类信息(包括成员方法和静态方法)、常量、静态变量、即时编译器编译后的代码缓存等数据
运行时常量池:存放编译生成的各种字面量和符号引用,运行期间也可能将新的常量放入池中
各部分简介:
- 程序计数器:
内存空间小,线程私有。字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成。
如果线程正在执行一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器的值则为 (Undefined)。此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。 - Java虚拟机栈
线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)。用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。 - 本地方法栈:
区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会有 StackOverflowError 和 OutOfMemoryError 异常。 - 堆
对于绝大多数应用来说,这块区域是 JVM 所管理的内存中最大的一块。线程共享,主要是存放对象实例和数组。内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。 - 方法区
属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 - 运行时常量池
属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用。编译器和运行期(String 的 intern() )都可以将常量放入池中。内存有限,无法申请时抛出 OutOfMemoryError。 - 直接内存
非虚拟机运行时数据区的部分
Java内存模型
Java堆和方法区是多个线程共享的数据区域。多个线程可以操作堆和方法区中的同一个数据。Java的线程间通过共享内存进行通信。
JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。
本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
JMM主要是保证共享内存部分的原子性、可见性、有序性,内存模型定义了共享内存中多线程程序读写宝座行为的规范。
内存模型解决并发问题主要采用两种方式: 限制处理器优化和使用内存屏障。
Java对象模型
Java对象在JVM中的存储有一定的结构,Java对象自身的存储模型就叫做Java对象模型。
总结
JVM内存结构,和JVM的运行时区域有关。 Java内存模型,和Java的并发编程有关。 Java对象模型,和Java对象在虚拟机中的表现形式有关。