运行时栈帧结构
栈帧:
局部变量表:
一组变量值存储空间。用于存放方法参数和方法内部定义的局部变量。 在Java程序被编译为Class文件时,就在方法的Code属性的max_locals数据项中 确定了方法所需要分配的最大局部变量表的容量。
单位:slot
存储数据类型和引用。
大小根据操作系统确定。32bit系统为32bit,64bit系统为64bit。存储数据类型时,如果小于32位使用一个slot;如果大于32位使用两个slot。
32位数据类型:
boolean,byte,char,short,int,float,refrence,returnAddress
64位数据类型:
long,double
复用:当一个变量的PC寄存器的值大于slot的作用域的时候,Slot是可以复用的。
代码优化:
局部变量没有初始值,声明时要赋值。
对象使用完之后指向为null,方便垃圾回收。
操作数栈:
运算的地方。
一个以字长为单位的数组。通过压栈和出栈来访问。
动态链接
静态链接:加载过程中间链接接转为直接链接
动态链接:每次运行期间都会转化为直接链接
返回地址
方法调用时记录调用方法地址,方法执行完毕之后跳回到所调方法位置。
附加信息
虚拟机规范允许具体虚拟机添加一些规范中没有的信息到栈帧中。这些信息取决于虚拟机的实现。
方法调用
方法调用并不等于方法的执行,方法调用阶段的唯一任务就是确定被调用方法的版本。
-
解析调用
方法在编译的时候就确定了,方法的调用版本在运行之前不变的。(静态方法,构造器方法,私有方法,final方法)涉及到的指令:invokespecial,invokestatic。 -
分派调用
-
静态分派调用
在编译器时期就确定方法的版本。如果存在函数重载,那么在调用是按照参数类型匹配最接近的方法。 -
动态分派调用
1.找到操作数栈顶的第一个元素所指向的对象的实际类型
2.如果在实际类型中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则直接返回这个方法的直接引用,查找过程结束,如果不通过,抛出异常。
-
动态语言支持
public class ScriptTest {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine se = sem.getEngineByName("JavaScript");
Object obj = se.eval("function add(a,b){ return a+b;} add(2,3)");
System.out.println(obj);
}
}