Java开发中每当我们在程序中使用new生成一个对象对象的引用存放在栈里而对象是存放在堆里的可以看出栈在Java核心的重要位置今天我们就继续深入Java核心这个系列为您介绍Java中的栈局部变量及其之间的关系
Java中的栈
每当启用一个线程时JVM就为他分配一个Java栈栈是以帧为单位保存当前线程的运行状态某个线程正在执行的方法称为当前方法当前方法使用的栈帧称为当前帧当前方法所属的类称为当前类当前类的常量池称为当前常量池当线程执行一个方法时它会跟蹤当前常量池
每当线程调用一个Java方法时JVM就会在该线程对应的栈中压入一个帧这个帧自然就成了当前帧当执行这个方法时它使用这个帧来存储参数局部变量中间运算结果等等
Java栈上的所有数据都是私有的任何线程都不能访问另一个线程的栈数据所以我们不用考虑多线程情况下栈数据访问同步的情况
像方法区和堆一样Java栈和帧在内存中也不必是连续的帧可以分布在连续的栈里也可以分布在堆里
Java栈的组成元素——栈帧
栈帧由三部分组成局部变量区操作数栈帧数据区局部变量区和操作数栈的大小要视对应的方法而定他们是按字长计算的但调用一个方法时它从类型信息中得到此方法局部变量区和操作数栈大小并据此分配栈内存然后压入Java栈
局部变量区 局部变量区被组织为以一个字长为单位从开始计数的数组类型为shortbyte和char的值在存入数组前要被转换成int值而long和double在数组中占据连续的两项在访问局部变量中的long或double时只需取出连续两项的第一项的索引值即可如某个long值在局部变量区中占据的索引时项取值时指令只需取索引为的long值即可
下面就看个例子好让大家对局部变量区有更深刻的认识这个图来自《深入JVM》
以下是引用片段
publicstaticintrunClassMethod(intilonglfloatfdoubledObjectobyteb){
return;
}
publicintrunInstanceMethod(charcdoubledshortsbooleanb){
return;
}
上面代码片的方法参数和局部变量在局部变量区中的存储结构如下图
上面这个图没什么好说的大家看看就会懂但是在这个图里有一点需要注意
runInstanceMethod的局部变量区第一项是个reference(引用)它指定的就是对象本身的引用也就是我们常用的this但是在runClassMethod方法中没这个引用那是因为runClassMethod是个静态方法
操作数栈和局部变量区一样操作数栈也被组织成一个以字长为单位的数组但和前者不同的是它不是通过索引来访问的而是通过入栈和出栈来访问的可把操作数栈理解为存储计算时临时数据的存储区域下面我们通过一段简短的程序片段外加一幅图片来了解下操作数栈的作用
以下是引用片段
inta=;
intb=;
intc=a+b;
从图中可以得出操作数栈其实就是个临时数据存储区域它是通过入栈和出栈来进行操作的
帧数据区除了局部变量区和操作数栈外Java栈帧还需要一些数据来支持常量池解析正常方法返回以及异常派发机制这些数据都保存在Java栈帧的帧数据区中
当JVM执行到需要常量池数据的指令时它都会通过帧数据区中指向常量池的指针来访问它
除了处理常量池解析外帧里的数据还要处理Java方法的正常结束和异常终止如果是通过return正常结束则当前栈帧从Java栈中弹出恢复发起调用的方法的栈如果方法又返回值JVM会把返回值压入到发起调用方法的操作数栈
为了处理Java方法中的异常情况帧数据区还必须保存一个对此方法异常引用表的引用当异常抛出时JVM给catch块中的代码如果没发现方法立即终止然后JVM用帧区数据的信息恢复发起调用的方法的帧然后再发起调用方法的上下文重新抛出同样的异常
栈的整个结构
在前面就描述过栈是由栈帧组成每当线程调用一个Java方法时JVM就会在该线程对应的栈中压入一个帧而帧是由局部变量区操作数栈和帧数据区组成那在一个代码块中栈到底是什么形式呢?下面是我从《深入JVM》中摘抄的一个例子大家可以看看
代码片段
执行过程中的三个快照
上面所给的图只想说明两件事情我们也可用此来理解Java中的栈
只有在调用一个方法时才为当前栈分配一个帧然后将该帧压入栈
帧中存储了对应方法的局部数据方法执行完对应的帧则从栈中弹出并把返回结果存储在调用方法的帧的操作数栈中