类在“加载 --> 验证 --> 准备 --> 解析 --> 初始化”这几个阶段完成后,就会用到执行引擎对我们的类进行使用,同时执行引擎将会使用到我们运行时数据区。每个JVM仅有一个运行时数据区。
一、运行时数据区结构
运行时数据区与内存
内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策略,保证了JVM的高效稳定运行。不同的JVM对于内存的划分方式和管理机制存在着部分差异。结合JVM虚拟机规范,来探讨一下经典的JVM内存布局。
我们通过磁盘或者网络IO得到的数据,都需要先加载到内存中,然后CPU从内存中获取数据进行读取,也就是说内存充当了CPU和磁盘之间的桥梁
二、PC寄存器
PC寄存器用于存放线程下一条指令的地址。保证了线程来回切换后,仍能正常执行。
- 每个线程都有自己的PC寄存器
- PC寄存器就一个地址空间,且随着线程的结束而释放,所以不会存在垃圾回收、OOM
- 寄存器的特点:指令更少,执行速度快,但是指令集(指令行数)多
三、JAVA虚拟机栈
3.1 栈运行原理
- 每个线程对应一个栈,栈由一个个栈帧组成,每个栈帧对应一个方法,不同线程中所包含的栈帧是不允许存在相互引用的。
- JVM直接对Java栈的操作只有两个,就是对栈帧的压栈和出栈,遵循先进后出(后进先出)原则。
- 在一条活动线程中,一个时间点上,只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧(栈顶栈帧)是有效的。这个栈帧被称为当前栈帧(Current Frame),与当前栈帧相对应的方法就是当前方法(Current Method),定义这个方法的类就是当前类(Current Class)。
- 执行引擎运行的所有字节码指令只针对当前栈帧进行操作。
- 如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
- 如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈帧,接着,虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧。
- Java方法有两种返回函数的方式:一种是正常的函数返回,使用return指令。另一种是方法执行中出现未捕获处理的异常,以抛出异常的方式结束。但不管使用哪种方式,都会导致栈帧被弹出。
3.2 栈帧内部结构
每个栈帧中存储着:
- 局部变量表(Local Variables)
- 操作数栈(Operand Stack)(或表达式栈)
- 动态链接(Dynamic Linking)(或指向运行时常量池的方法引用)
- 方法返回地址(Return Address)(或方法正常退出或者异常退出的定义)
- 一些附加信息
3.2.1 局部变量表
- 局部变量表定义为一个一维数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型、对象引用(reference),以及returnAddress类型
- 局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maximum local variables数据项中。在方法运行期间是不会改变局部变量表的大小的
- 局部变量表中的变量只在当前方法调用中有效,当方法调用结束后,随着方法栈帧的销毁,局部变量表也会随之销毁。【不同线程中的栈帧无法相互引用,且局部变量表仅在当前栈帧有效,所以局部表量表中数据是线程安全的】
局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收。
3.2.2 局部变量表的存储结构--变量槽Solt
- 局部变量表,最基本的存储单元是Slot(变量槽),参数值的存放总是从局部变量数组索引 0 的位置开始,到数组长度-1的索引结束。
- 32位以内的类型只占用一个slot(包括引用类型、returnAddress类型),64位的类型占用两个slot(1ong和double)。
--------byte、short、char在储存前辈转换为int,boolean也被转换为int,0表示false,非0表示true
--------long和double则占据两个slot
- 当一个实例方法被调用的时候,它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个slot上,如果需要访问局部变量表中一个64bit的局部变量值时,只需要使用前一个索引即可。
- 如果当前帧是由构造方法或者实例方法创建的,那么该对象引用this将会存放在index为0的slot处,其余的参数按照参数表顺序继续排列。(this也相当于一个变量)【静态方法无this变量】
举例1--变量顺序存放+静态函数无this
举例2--this+64位占用2个Solt
对应举例代码
public String test2(Date dateP, String name2) {
dateP = null;
name2 = "songhongkang";
double weight = 130.5;//占据两个slot
char gender = '男';
return dateP + name2;
}
对应Solt图
四、方法中定义的变量是否线程安全?