说一下运行时数据区
关联课程内容
- 基础篇-程序计数器
- 基础篇-栈
- 基础篇-堆
- 基础篇-方法区
- 基础篇-直接内存
回答路径
- 程序计数器
- 栈
- 堆
- 方法区
直接内存(可选)
运行时数据区指的是JVM所管理的内存区域,其中分成两大类:
- 线程共享 – 方法区、堆
- 线程不共享 – 本地方法栈、虚拟机栈、程序计数器
直接内存主要是NIO使用,由操作系统直接管理,不属于JVM内存。
# 程序计数器
程序计数器(Program Counter Register)也叫PC寄存器,每个线程会通过程序计数器记录当前要执行的的字节码指令的地址。主要有两个作用:
1、程序计数器可以控制程序指令的进行,实现分支、跳转、异常等逻辑。
2、在多线程执行情况下,Java虚拟机需要通过程序计数器记录CPU切换前解释执行到那一句指令并继续解释运行。
# 栈 - Java虚拟机栈
Java虚拟机栈采用栈的数据结构来管理方法调用中的基本数据,先进后出 ,每一个方法的调用使用一个栈帧来保存。每个线程都会包含一个自己的虚拟机栈,它的生命周期和线程相同。
栈帧主要包含三部分内容:
1、局部变量表,在方法执行过程中存放所有的局部变量。
2、操作数栈,虚拟机在执行指令过程中用来存放临时数据的一块区域。
如下图中,iadd指令会将操作数栈上的两个数相加,为了实现i+1
。最终结果也会放到操作数上。
3、帧数据,主要包含动态链接、方法出口、异常表等内容。
动态链接:方法中要用到其他类的属性和方法,这些内容在字节码文件中是以编号保存的,运行过程中需要替换成内存中的地址,这个编号到内存地址的映射关系就保存在动态链接中。
方法出口:方法调用完需要弹出栈帧,回到上一个方法,程序计数器要切换到上一个方法的地址继续执行,方法出口保存的就是这个地址。
异常表:存放的是代码中异常的处理信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置。
# 本地方法栈
Java虚拟机栈存储了Java方法调用时的栈帧,而本地方法栈存储的是native本地方法的栈帧。
在Hotspot虚拟机中,Java虚拟机栈和本地方法栈实现上使用了同一个栈空间。本地方法栈会在栈内存上生成一个栈帧,临时保存方法的参数同时方便出现异常时也把本地方法的栈信息打印出来。
# 堆
- 一般Java程序中堆内存是空间最大的一块内存区域。创建出来的对象都存在于堆上。
- 栈上的局部变量表中,可以存放堆上对象的引用。静态变量也可以存放堆对象的引用,通过静态变量就可以实现对象在线程之间共享。
- 堆是垃圾回收最主要的部分,堆结构更详细的划分与垃圾回收器有关。
# 方法区
方法区是Java虚拟机规范中提出来的一个虚拟机概念,在HotSpot不同版本中会用永久代或者元空间来实现。方法区主要存放的是基础信息,包含:
1、每一个加载的类的元信息(基础信息)。
2、运行时常量池,保存了字节码文件中的常量池内容,避免常量内容重复创建减少内存开销。
3、字符串常量池,存储字符串的常量。
# 直接内存
直接内存并不在《Java虚拟机规范》中存在,所以并不属于Java运行时的内存区域。在 JDK 1.4 中引入了 NIO 机制,由操作系统直接管理这部分内容,主要为了提升读写数据的性能。在网络编程框架如Netty中被大量使用。
要创建直接内存上的数据,可以使用ByteBuffer。
语法: ByteBuffer directBuffer = ByteBuffer.allocateDirect(size);
# 总结
运行时数据区指的是JVM所管理的内存区域,其中分成两大类:
- 线程共享 方法区、堆
方法区:存放每一个加载的类的元信息、运行时常量池、字符串常量池。
堆:存放创建出来的对象。
- 线程不共享 – 本地方法栈、虚拟机栈、程序计数器
本地方法栈和虚拟机栈都存放了线程中执行方法时需要使用的基础数据。
程序计数器存放了当前线程执行的字节码指令在内存中的地址。
直接内存主要是NIO使用,由操作系统直接管理,不属于JVM内存。