1.概述
不同虚拟机实现里面,执行引擎在执行Java代码时可能有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择。
2运行时栈帧结构
对于执行引擎来讲,活动线程中只有栈顶的栈帧是有效的,称为当前栈帧,栈帧所关联的方法称为当前方法
2.1局部变量表
用于存放方法参数和方法内部定义的局部变量。Java程序在被编译为Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需分配的最大局部变量表的容量。
局部变量表的容量以变量槽(Variable Slot,简称Slot)为最小单位。
虚拟机通过索引定位方式使用局部变量表,索引值范围从0开始,到最大Slot数量。
局部变量表中的Slot是可重用的,
!!赋null值的操作在经过虚拟机JIT编译器优化后会被消除掉,实际上是没有意义的。
!!局部变量必须要赋初始值。
2.2操作数栈
栈深度不会超过max_stacks数据项中设定的最大值。
方法刚开始执行时,方法的操作数栈是空的。做算术运算时是通过操作数栈来进行的;在调用其他方法时,是通过操作数栈来进行参数传递的。
!!Java虚拟机的解释执行引擎称为“基于栈(操作数栈)的执行引擎”。
2.3动态链接
静态解析,类加载阶段或第一次使用时,符号引用转化为直接引用。
动态连接,每一次运行期间。
2.4方法返回地址
正常完成出口<—>异常完成出口(地址由异常处理器表确定)
2.5附加信息
3.方法调用
不等同于方法执行,唯一任务是确定被调用方法的版本。
3.1解析
所有方法调用中的目标方法在Class文件里都是一个常量池中的符号引用。类加载解析阶段会将其中一部分符号引用转化为直接引用(调用目标在程序代码写好、编译器进行编译时就必须确定下来)。如:静态方法和私有方法。
Java虚拟机里提供了四条方法调用字节码指令:
invokestatic:调用静态方法;
invokespecial:调用实例构造器<init>()
方法、私有方法、父类方法;
invokevirtual:调用所有的虚方法
invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
能被invokestatic和invokespecial指令调用的方法,都可在解析阶段确定唯一的调用版本,符合条件的有静态方法、私有方法、实例构造器和父类方法四类。外加final方法。
3.2分派
Java具备面向对象三个基本特征:继承、封装和多态。
3.2.1静态分派
两个静态类型相同、实际类型不同的变量,虚拟机(准确说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据的。
静态分派发生在编译阶段。
3.2.2动态分派
动态分派和多态性的重写有着密切的关联。
3.2.3单分派与多分派
方法的接收者与方法的参数统称为方法的宗量。单分派根据一个宗量对目标方法进行选择;多分派根据多个宗量对目标方法进行选择。
JDK1.6时期的Java语言是一门静态多分派、动态单分派的语言。
4.基于栈的字节码解释执行引擎
4.1解释执行
4.2基于栈的指令集与基于寄存器的指令集
基于栈的指令集优点:可移植,代码紧凑。
缺点:由于指令数量和内存访问(直接内存访问)的原因,导致栈架构指令集的执行速度相对较慢。
5.OSGI:灵活的类加载器架构
OSGI里,Bundle之间的依赖关系从传统的上层模块依赖底层模块转变为平级模块直接的依赖。
一个模块里,只有被Export过的Package才可被外界访问,其他的Package和Class将被隐藏起来。
!!如果出现Bundle A依赖Bundle B的Package B,而Bundle B又依赖Bundle A的Package A,这两个Bundle进行类加载时就很容易发生死锁。当Bundle A加载Bundle B的类时,首先需要锁定当前类加载器的实例对象(java.lang.ClassLoader.loadClass()是synchronized方法),然后把请求委派给Bundle B的加载器处理。
OSGI也带来了线程死锁和内存泄露的风险。
6.Retrotranslator:跨越JDK版本
Java逆向移植。
虚拟机字节码执行引擎
最新推荐文章于 2024-09-05 23:10:07 发布