Overview:
- 虚拟机的执行引擎是由自己实现的,因可以自行制定指令集和执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令集格式。
- jvm都是:输入是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果
- 栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构。
- 执行引擎运行的所有字节码指令都只针对当前栈帧(栈顶栈帧)进行操作。
栈帧结构:
局部变量表:
- 容量以Slot为最小单位。一个Slot应可以存的下32位以内的变量类型。对于64位的数据类型,用两个连续的slot空间来存储。Slot可以重用。
- 通过引用达成两点:从此引用中直接或间接地查找到对象在java堆中的数据存放的真实地址索引;此引用中直接或间接地查找到对象所属数据类型在方法区中的存储的类型信息。
- jvm通过索引定位的方式使用局部变量表,在方法执行的时候,虚拟机使用局部变量表完从参数值到参数变量列表的传递过程。
- 局部变量不会被自动初始化;类变量会在准备阶段和初始化阶段被赋值。
操作数栈(LIFO)
- 用于算数运算和调用其他方法时的参数传递。
- 两个栈帧可能相互独立也可能共享部分数据。
动态连接
- 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。
- 运时时常量池中的一些符号引用在每一次运行期间转换为直接引用,这部分称为动态连接。
方法返回地址
- 方法退出的过程实际上就等于把当前栈帧出栈。
方法调用:
- 不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本.
解析:将编译期可知,运行期不可变的方法,也称为非虚方法(如私有方法,静态方法,构造器,父类方法,final方法)的符号引用解析为直接引用。
分派:
静态分派
- 在重载的时候是通过参数的静态类型而不是实际类型作为判定依据的。并且静态类型编译期可知。
- 所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。其典型应用是方法重载。
- 静态分派发生在编译阶段,因为是根据2个宗量进行选择,所以静态分派属于多分派
动态分派
- 在运行期间根据实际类型确定方法执行版本的分配过程称为动态分配。
- 使用虚方法表(用于索引)来代替元数据查找(因为查找太频繁,所以用表和索引来提高性能)
- 动态分配是单分配,因为编译阶段已经确定参数类型,此时只依赖对象实际类型.
动态语言
- 动态语言的关键特征是它的类型检查的主体过程是在运行期而不是编译期间。
- 变量没有类型,变量值才有类型。
- 静态语言由于在编译期间确定变量类型,所以编译器可以提供良好的类型检查。而动态语言可以提供灵活性,也可以生成更加简介,清晰的代码。
- 从本质上说,Reflection和MethodHandle机制都是在模拟方法调用。
但Reflection是在模拟java代码层次的方法调用,而MethodHandle是在模拟字节码层次的方调用。
基于栈的字节码执行引擎
- javac编译器完成了程序代码经词法分析,语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程。
- 解释器在虚拟机内部。
- java编译器输出的指令流,基本上是一种基于栈的指令集架构。依赖操作数栈进行工作。
- 基于栈的指令集主要的优点是可移植,缺点是执行速度相对慢。
原因:完成相同功能需要更多指令,如出栈和入栈。栈实现在内存中,栈的频繁访问相当于频繁访问内存。