5方法运行时的栈帧
- 栈帧是虚拟机栈中用于执行方法调用的数据结构,是虚拟机栈中的栈元素。
- 一个栈帧存储了一个方法的局部变量表、操作数栈、动态链接、方法返回地址等信息。
- 每一个方法的调用与返回都对应着一个栈帧在虚拟机栈中的入栈出栈操作。
- 栈帧中局部变量表的大小、操作数栈的深度都在编译期确认了。因此栈帧的内存大小已经确定。
-
局部变量表
- 局部变量表用于存储方法参数和方法内定义的局部变量。
- 局部变量表以变量槽(Slot)为基本单位,slot大小通常为32/64位,取决于具体实现。
- double,long可以存在两个slot中。
- slot存储:boolean,byte,char,short,int,float,reference,returnAddress,long,double。
- reference代表一个对象的引用,通过其可以直接或间接的找到对象在堆中的位置以及对象所属类型在方法区中的存储的类型信息。
- 方法执行时通过局部变量表完成参数值到参数变量列表的传递过程。非static方法第一个slot存储对象的实例(也就是this引用)。
- slot是可以重用的,slot可以在超出变量作用域后存放之后的变量。
- 局部变量不像类变量一样存在准备阶段(附上系统预设的初值),因此必须进行初始化。
-
操作数栈
- jvm执行引擎是栈结构,因此利用操作数栈来存放当前执行的语句的操作数。
- 例如一个add操作将对应着:栈顶两个元素出栈,相加后,结果入栈。
- 操作数栈存储的元素必须与字节码指令严格对应。
-
方法返回地址
- 方法退出只有两种方式:
- 方法返回指令:可能有返回值传递给上层方法调用者。
- 出现未处理的异常:不会有返回值给上层调用者。
- 方法退出时相当于把当前栈帧出栈:
- 恢复上层方法的局部变量表、操作数栈。
- 并把有的返回值压入调用者的操作数栈中。
- 调整PC寄存器。
- 方法退出只有两种方式:
-
方法调用
-
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,这个引用用于实现方法调用中的动态链接。
-
用于确认调用的是哪个方法。class文件编译时只保存了调用的方法的符号引用。因此需要在类加载/运行期才能确定方法的直接引用。
-
类加载阶段确认直接引用:(得到直接引用的过程称作解析)
- 这类方法必须是运行期不改变的
- 主要为:静态方法,私有方法,实例构造器,父类方法,final方法。
- 这类方法不可通过继承或别的方式重写其他版本。解析阶段便会唯一确定调用版本。
-
运行时确认直接引用:(称作分派)
-
静态分派:
-
java中方法重载便是静态分派,静态分派发生在编译期间。
-
方法重载依靠参数的静态类型来定位方法。
-
Human man=new Man(); //Human是静态类型,是编译器可知的。 //Man是实际类型,是运行时才可知的。 void say(Human human){} void say(Man man){} //若调用上述两个重载的方法 say(man);//根据man的静态类型是Human则调用的实际上是第一个方法 say((Man)man);//强制类型转换导致了man的静态类型变换调用的是第二个
-
-
静态类型匹配顺序:char->int->long->float->double->包装类->父类或接口->可变长参数
-
-
动态分派:
-
java中方法的重写是动态分派。
-
以一个对象调用方法为例:
- 通过寻找这个对象的实际类型,在实际类型中寻找方法
- 找到:判断访问权限
- 没找到:在其各个父类中进行寻找。
- 通过寻找这个对象的实际类型,在实际类型中寻找方法
-
因此多态中,对象调用一个重写过的方法时是优先调用自身重写的版本,其次才是父类的。
-
因为动态连接每次都需要查找因此在方法区中创建了一个名叫虚方法表的数据结构。以提高寻找效率。
-
它并不是记录所有方法的表。它是为动态分派服务,不会记录静态方法和构造函数和私有方法。
-
-
-
来源:CSDN
作者:tank59he
链接:https://blog.csdn.net/hu853996234/article/details/103771128