概述:
运行时数据区,Java虚拟机(JVM)中用于存储程序运行期间所需的各种数据的内存区域。主要包括程序计数器,java虚拟机栈,本地方法栈,堆,方法区。
1. 线程不共享
(1)程序计数器
a. 定义
记录当前要执行的的字节码指令的地址
b. 内容
类加载器将字节码加载到内存后,会将原文件中的偏移量转换成内存地址。每一条字节码指令都会拥有一个内存地址。代码执行时,程序计数器会记录下一行字节码指令的地址。
注:由于程序计数器存储固定长度的内存地址,因此不会出现内存溢出。
(2)虚拟机栈
a. 定义
采用栈的数据结构来管理方法调用中的基本数据,每个方法的调用链会压入一个栈中,执行时先进后出(FILO)。
b. 组成
1) 局部变量表
局部变量表中保存了实例方法的this对象,方法参数,局部变量。
2) 操作数栈
字节码指令执行过程中用来存放中间数据的一块区域,是一种栈式结构。
将局部变量表中对应索引位的值加载到操作数栈中进行计算,然后将得到的值再保存到局部变量表中对应索引位中。
3) 帧数据
帧数据主要包含动态链接、方法出口、异常表的引用。
动态链接:字节码中的符号引号到运行时常量池的内存地址的映射关系。
方法出口:方法执行完后,程序计数器将指向下一条将要执行的字节码指令的地址。
异常表:存放代码中异常的处理信息。
注:java虚拟机栈会出现内存溢出StackOverFlowError,常见的如:递归调用
可以通过-Xss来指定栈的大小
(3)本地方法栈
本地方法栈结构与虚拟机栈类似,服务于本地方法,管理本地方法执行所需的数据。
2. 线程共享
(1)堆
a. 定义
存储对象的内存区域,堆内存是线程共享的。分为年轻代与老年代,年轻代又分为伊甸园区和存活区。
可以通过-Xmx设置堆内存的最大值,通过-Xms设置堆内存的初始值。
注:堆内存会出现内存溢出OutOfMemoryError: Java heap space。设置堆内存大小时,建议将-Xmx与-Xms设置成一样的值,这样可以减少扩容的开销。
(2)方法区
a. 定义
用于存储每个类的元信息,运行时常量池,字符串常量池。
b. 组成
类的元信息:包括类的基本信息,接口,字段,方法,属性。在类的字节码被类加载器加载到方法区时,会在方法区中创建一个instanceKlass的对象,该对象还会包含方发表,方发表是实现多态的基础。
运行时常量池:字节码中常量池成为静态常量池,通过符号引用进行常量查找。在字节码被加载到方法区后,静态常量池会变成运行时常量池,符号引用会变成内存地址,通过内存地址查找常量。
字符串常量池:存储代码中定义的常量字符串内容。
注:JDK7之前,方法区存储在堆的永久代中,字符串常量池存储在方法区。JDK7时,字符串常量池从方法区中移到了堆中。JDK8时,方法区移到了元空间中,即直接内存中。方法区会出现内存溢出,OutOfMemoryError:MetaSpace。