摘要:对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是最有效的称为当前栈帧与这个栈帧相关联的方法称为当前方法。执行引擎运行的所有的字节码指令都只针对当前栈帧进行操作。
栈帧数据结构
栈帧(Stack Frame)是用来支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。
栈帧(Stack Frame)存储了方法的局部变量表、操作数栈、动态连接、和方法返回地址、额外的附加信息。
每个方法在执行的同时,都会创建一个栈帧(Stack Frame)。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。
栈帧的内存分配在编译程序代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到方法表的Code属性中了,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体虚拟机的实现。
当前栈帧(Current Stack Frame)一个线程中的方法调用链可能会很长,很多方法都同时处于执行状态。对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是最有效的,称为当前栈帧(Current Stack Frame),与这个栈帧相关联的方法称为当前方法。执行引擎运行的所有的字节码指令都只针对当前栈帧进行操作。在概念模型上,典型的栈帧结构图如下:
局部变量表局部变量表(Local Variable Table)是一组变量值存贮空间,用于存放方法参数和方法内定义的局部变量。在Java程序编译为Class文件时候,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的局部变量表的最大容量。单位为Slot
局部变量表的容量以变量槽(Variable Slot)为最小单位。每个变量槽都可以存储32位长度的内存空间,例如boolean、byte、char、short、int、float、reference。
对于64位长度的数据类型(long,double),虚拟机会以高位对齐方式为其分配两个连续的Slot空间,也就是相当于把一次long和double数据类型读写分割成为两次32位读写。
整型 | 字节(b) | bite(位) |
---|---|---|
byte | 1 | 1*8 |
short | 2 | 2*8 |
int | 4 | 4*8 |
long | 8 | 8*8 |
浮点型 | 字节(b) | bite(位) |
---|---|---|
float | 4 | 4*8 |
double | 8 | 8*8 |
char类型 | 字节(b) | bite(位) |
---|---|---|
char | 2 | 2*8 |
布尔型 | 字节(b) | bite(位) |
---|---|---|
boolean | 1 | 1*8 |
其中:8bit=1b [一个Byte等于8个bit(位)],1024b=1kb
为了节省栈帧空间,局部变量表中的Slot是可以重用的,方法体中定义的变量,其作用域并不一定会覆盖整个方法体,如果当前字节码PC计数器的值已经超过了某个变量的作用域,那么这个变量对应的Slot就可以交给其他变量使用。
优点 : 节省栈帧空间。
缺点 : 影响到系统的垃圾收集行为。(如大方法占用较多的Slot,执行完该方法的作用域后没有对Slot赋值或者清空设置null值,垃圾回收器便不能及时的回收该内存。)
操作数栈(operand Stack)也常称为操作栈,它是一个后入先出栈。和局部变量表一样,操作数栈的最大深度也在编译的时候写入到Code属性的max_stacks中。
操作数栈的每一个元素可用是任意的Java数据类型,包括long和double。32位数据类型所占的栈容量为1,64位数据类型占用的栈容量为2。
当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法执行的过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈 / 入栈操作。例如,在做算术运算的时候是通过操作数栈来进行的,又或者在调用其它方法的时候是通过操作数栈来进行参数传递的。
动态连接每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。
Class文件的常量池中存在大量符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数,这些符号引用一部分在类加载阶段中的解析阶段会转为直接引用,这种转化也称为静态解析。另外的一部分将在每一次运行时期转化为直接引用。这部分称为动态连接。
方法返回地址当一个方法开始执行后,只有2种方式可以退出这个方法 :
方法返回指令 : 执行引擎遇到一个方法返回的字节码指令,这时候有可能会有返回值传递给上层的方法调用者,这种退出方式称为正常完成出口。
异常退出 : 在方法执行过程中遇到了异常,并且没有处理这个异常,就会导致方法退出。
无论采用任何退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信。用来帮助恢复它的上层方法的执行状态。
方法退出的过程实际上就等于把当前栈帧出栈,因此退出可能执行的操作有:
1.恢复上层方法的局部变量表和操作数栈
2.把返回值(如果存在返回值)压入调用者栈帧的操作数栈中
3.调整PC计数器的值以指向方法调用指令后面的一条指令
虚拟机规范允许具体的虚拟机实现增强一些规范里没有描述的信息到栈帧之中,例如与调试相关的信息,这部分信息取决于具体的虚拟机实现。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/75883.html
摘要:在本文,笔者将与大家概览的体系结构与工作方式。将第条和第条指令分别是将两个局部变量入栈,然后相加。最后一条指令是,这条指令执行完后当前的这个方法对应的这些部件会被回收,局部变量区的所有值将全部释放,寄存器会被销魂,在栈中与这个方 Java之所以号称一次编译,到处运行,主要原因是JVM屏蔽了各个计算机平台相关的软件(大多指系统)或者硬件之间的差异,使得与平台相关的耦合统一由JVM提供者来...
摘要:每个栈帧中包括局部变量表用来存储方法中的局部变量非静态变量函数形参。操作数栈虚拟机的解释执行引擎被称为基于栈的执行引擎,其中所指的栈就是指操作数栈。指向运行时常量池的引用存储程序执行时可能用到常量的引用。 本篇文章转自微信公众号:Java后端技术 学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出...
摘要:操作数栈虚拟机的解释执行引擎被称为基于栈的执行引擎,其中所指的栈就是指操作数栈。基本数据类型的静态变量前面提到方法区用来存储一些共享数据,因此基本数据类型的静态变量名以及值存储于方法区的运行时常 本文旨在用最通俗的语言讲述最枯燥的基本知识 学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而...
摘要:虚拟机在执行程序的过程中会把它所管理的内存划分为若干个不同的数据区域。栈帧栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧的概念结构如下运行时数据区脑图高 这里我们先说句题外话,相信大家在面试中经常被问到介绍Java内存模型,我在面试别人时也会经常问这个问题。但是,往往都会令我比较尴尬,我还话音未落,面试者就会背诵一段(Java虚拟...
摘要:内存模型首先介绍下程序具体执行的过程源代码文件后缀会被编译器编译为字节码文件后缀由中的类加载器加载各个类的字节码文件,加载完毕之后,交由执行引擎执行在整个程序执行过程中,会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被 [TOC] JVM内存模型 首先介绍下Java程序具体执行的过程: Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(....
阅读 1275·2023-04-25 19:33
阅读 1153·2021-10-21 09:39
阅读 3625·2021-09-09 09:32
阅读 2586·2019-08-30 10:58
阅读 1579·2019-08-29 16:17
阅读 859·2019-08-29 15:29
阅读 2864·2019-08-26 11:55
阅读 2631·2019-08-26 10:33