JVM之虚拟机栈
大家好,我是程序员学长,专注分享大数据、算法、java、python等相关知识,欢迎和我一起交流学习。
今天我们来聊一下 JVM 中虚拟机栈的概念。
虚拟机栈
与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的生命周期与线程相同。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧,对应着一次次的方法调用。
开发者遇到的异常
Java虚拟机规范允许Java栈的大小是动态的或者固定不变的。
如果采用固定大小的Java虚拟机栈,那么每一个线程的Java虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机将会抛出一个StackOverflowError异常。
如果采用动态扩展的Java虚拟机栈,当在尝试动态扩展时无法申请到足够的内存时,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈时,那么Java虚拟机会抛出一个OutOfMemoryError异常。
栈的存储单位
每个线程都有自己的栈,栈中的数据都是以栈帧的格式存在。在这个线程上正在执行的每个方法都各自对应一个栈帧。栈帧是一个内存区块,用于存储局部变量表、操作数栈、动态链接、方法返回地址等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表
局部变量表:Local Variables,又称为局部变量数组或本地变量表。
定义为一个数字数组,主要是用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型、对象引用(reference)、以及returnAddress类型。
由于局部变量表是建立在线程的虚拟机栈上的,是线程的私有数据,因此不存在数据安全问题。
局部变量表所需要的容量大小是在编译期确定下来的,并保存在方法的 Code 属性 maximum local variables 数据项中。在方法运行期间是不会改变局部变量表的大小的。
方法嵌套调用次数是由栈的大小决定的。一般来说,栈越大,方法嵌套调用次数就越多。对于一个函数而言,它的参数和局部变量越多,使得局部变量表越大,它对应的栈帧也就越大,从而函数调用就会占用更多的栈空间,导致其嵌套调用次数就会减少。
局部变量表中的变量只有在当前方法调用中有效。在方法执行时,虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。当方法调用结束后,随着方法栈帧的销毁,局部变量表也随之销毁。
关于Slot的理解
局部变量表中最基本的存储单位是Slot(变量槽),局部变量表中存放编译期可知的各种基本类型(8种),引用类型(reference)、returnAddress 类型的变量。
在局部变量表中,32位以内的类型只占用一个 Slot (包括 returnAddress类型),64位的类型( long 和 double)占用两个slot。
byte、short、char 在存储前被转换为 int,boolean也被转换为 int , 0 表示false,非0 表示 true。long 和 double 则占据两个 slot。
JVM会为局部变量表中的每一个Slot分配一个访问索引,通过这个索引即可成功的访问到局部变量表中指定的局部变量值。
当一个实例方法被调用时,它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个Slot上。如果要访问局部变量表中一个64位的局部变量值时,只需要使用前一个索引即可。
如果当前栈帧是由构造方法或者实例方法创建的,那么该对象引用this将会存放在index为0的Slot处,其余的参数按照参数表顺序继续排列。
Slot的重复利用
栈帧中的局部变量表中的槽位是可以重用的,如果一个局部变量过了其作用域,那么在其作用域之后申明的新的局部变量就很有可能会复用过期局部变量的槽位,从而达到节省资源的目的。
public class test {
public void local(){
{
int a=1;
System.out.println(a);
}
//复用局部变量a的槽位
int b=2;
}
}
操作数栈
每一个独立的栈帧除了包含局部变量表以外,还包含一个后进先出的操作数栈,也可以称之为表达式栈。
操作数栈,在方法执行的过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈和出栈。
操作数栈,主要用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。
每一个操作数栈都有一个明确的栈深度用于存储数值,其所需的最大深度是在编译期就定义好了,保存在方法的Code属性中,为maxstack值。
栈中元素可以是任何的Java数据类型。
- 32 bit的类型占用一个栈单位深度
- 64 bit的类型占用二个栈单位深度
操作数栈并非采用访问索引的方式来进行数据访问的,而是只能通过标准的入栈和出栈操作来完成一次数据访问。
操作数栈中的元素的数据类型必须与字节码指令的序列严格匹配,这由编译器在编译期间进行验证,同时在类加载过程中的类检验阶段的数据分析阶段要再次验证。另外我们常说,java虚拟机的解释引擎是基于栈的执行引擎,其中的栈就是指操作数栈。
- 点赞
- 收藏
- 关注作者
评论(0)