一、局部变量表
- 局部变量表也被称之为局部变量数组或本地变量表;
- 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型,对象引用(reference),以及returnAddress类型;
- 由于局部变量表是建立在线程的栈上,它是线程的私有数据,因此不存在数据安全的问题;
- 局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maximum local variables数据项中,在方法运行期间是不会改变局部变量表的大小的。
- 方法嵌套调用的次数是由栈的大小决定的,一般来说,栈越大,方法嵌套调用的次数越多,对于一个参数来说,它的参数和局部变量越多,使得局部变量表膨胀,它的栈帧就会越大,以满足方法调用所需传递的信息量增大的需求,进而函数调用就会占用更多的栈空间;
- 局部变量表中的变量只在当前方法调用中有效,在方法执行时,虚拟机通过局部变量表完成参数值到参数变量列表的传递过程,当方法调用结束之后,随着方法栈帧的销毁,局部变量表也会随之销毁;
二、使用IDEA查看局部变量表
- 可以利用javap命令对字节码文件进行解析查看main()方法对应栈帧的局部变量表;
- 可以使用jclasslib byte viewcoder插件查看方法内部的字节码信息剖析;
以上截图中的< init >就相当于是构造器
比如Java代码行号为6:
对应的字节码指令在第8行:
三、变量槽slot的理解与演示
- 参数值的存放总是在局部变量数组的index[0]开始,到index[length - 1]的索引节点结束;
- 局部变量表最基本的存储单元是slot(变量槽);
- 局部变量表中存放的是编译期即可知道的基本数据类型(8种),引用类型(reference),returnAddress类型的变量;
- 在局部变量表中,32位以内的类型只占用一个slot(包括returnAddress类型),64位的类型(long和double)占用两个slot;
1. byte,short,char在存储前被转换为int,boolean也会被转换为int,0表示false,非0表示true;
2. long和double则占据两个slot;
- JVM会为局部变量表中的每一个slot都分配一个访问索引,通过这个索引就可以成功访问到局部变量表中指定的局部变量值;
- 当一个实例方法被调用的时候,它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个slot上;
- 如果需要访问局部变量表中的一个64bit的局部变量值时,只需要使用前一个索引即可;
- 如果当前栈帧是由构造方法或者实例方法创建的,那么该对象引用this将会存放在index为0的slot处,其余的参数按照参数表顺序继续排列;
- 静态方法中不能引用this,因为this变量不存在于静态方法的局部变量表中;
- slot的重复利用:栈桢中的局部变量表的槽位是可以重复利用的,如果一个局部变量过了其作用域,那么在其作用域之后声明的局部变量就很有可能会复用过期局部变量的槽位,从而达到节省资源的目的;
private void test2() {
int a = 0;
{
int b = 0;
b = a+1;
}
//变量c使用之前以及经销毁的变量b占据的slot位置
int c = a+1;
}
上述代码所对应的栈帧的局部变量表中一共有多少个slot,或者说局部变量表的长度是几?(3)
变量b的作用范围:
{
int b = 0;
b = a+1;
}
this占0号,a独占1号,c重复使用了b的槽号
四、静态变量与局部变量的对比
变量的分类:
- 按照数据类型分类:
1.1. 基本数据类型
1.2. 引用数据类型 - 按照在类中声明的位置分:
2.1. 成员变量:在使用前,都经历过默认初始化赋值;static修饰的话称为类变量:类加载链接(linking)的准备(preparation)阶段给各个类变量默认赋0值——>初始化阶段initialization给类变量显式赋值即静态代码块赋值;不被static修饰的称为实例变量:随着对象的创建,会在堆空间分配实例变量空间,并进行默认赋值;
2.2. 局部变量:在使用前,必须进行显式赋值,否则编译不会通过; - 补充说明
1. 在栈桢中,与性能调优关系最为密切的部分就是局部变量表。在方法执行时,虚拟机使用局部变量表完成方法的传递;
2. 局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收;