“虚拟机”是一个相对“物理机”的概念,这两种机器都有代码执行能力。
物理机的执行引擎是直接建立在处理器、硬件、指令集和操作系统层面上的。
虚拟机的执行引擎由自己实现,自行制定指令集与执行引擎的结构体系,并可执行不被硬件直接支持的指令集格式。
在虚拟机规范中,制定了虚拟机字节码执行引擎的概念模型,为各种虚拟机执行引擎的统一外观。
执行引擎在执行java代码时候可能会有解释执行和编译执行两种模式。
输入:字节码 处理:字节码解析 输出:执行结果
虚拟机的方法调用
虚拟机的字节码执行
栈幀:存在于虚拟机栈内存中,用于支持虚拟机进行方法调用和方法执行的数据结构。为虚拟机栈中的栈元素。
存储数据:局部变量表、操作数栈、动态链接和方法返回地址。
方法调用过程即一个栈幀在虚拟机栈里从入栈到出栈的过程。
局部变量表的大小、操作数栈深度,在编译期间已经确定,这些大小、深度写入了方法表的Code属性中,因此一个栈幀需要分配多少内存,不会受到程序运行
期间变量数据的影响,而仅仅取决于具体的虚拟机实现。
一个线程中的方法调用链很长,很多方法都同时处于执行状态,对于执行引擎来说,在活动线程中,只有位于栈顶的栈幀才是有效的,称为当前栈幀,与这个栈幀相关的方法称为当前方法。
执行引擎的所有字节码指令都只针对当前栈幀进行操作。
栈幀概念模型: 当前线程 线程1线程2
当前栈幀当前栈幀 当前栈幀
局部变量表 局部变量表局部变量表
操作数栈操作数栈 操作数栈
动态链接动态链接 动态链接
方法返回地址 方法返回地址 方法返回地址
.......................... .............
栈幀N 栈幀N 栈幀N
........................ ............
栈幀2栈幀2 栈幀2
栈幀1栈幀1 栈幀1
局部变量表:存放方法参数和方法内部局部变量,容量:在java程序编译为class文件时,就在方法的code属性的max_locals数据项中确定了该方法的所需分配的局部变量表的最大容量。
变量槽(Slot)是局部变量表中最小单位。
操作数栈:后入先出栈,可用户算术运算或调用其他方法时候进行参数传递。
例子:整数加法的字节码指令iadd在运行时候操作数栈中最接近栈顶的两个元素已经存入两个int型的数值,当执行这个指令时,会将这两个int值出栈并相加,然后将相加后的结果入栈。
动态链接:
方法返回地址:即当前栈幀出栈,退出时执行操作:恢复上层方法的局部变量表和操作数栈,把返回值压入调用者栈幀的操作数栈中,调整计数器的值指向后一条指令。
方法调用过程:
方法调用概念:确定被调用方法的版本
解析:所有方法调用中的目标方法在class文件里都有一个常量池中的符号引用,在类记载的解析阶段,会将其中的一部分符号引用转化为直接引用,这种解析能成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期间是不可改变的。比如:静态方法和私有方法两大类,前者与类型直接关联,后者在外部不可被访问,这两种方法各自的特点决定了他们都不可能通过继承或别的方式重写其他版本,因为他们都适合在类加载阶段进行解析。
java虚拟机5条方法调用字节码指令:
1、invokestatic :调用静态方法。
2、invokespecial:调用实例构造方法、私有方法、父类方法。
3、invokevirtual:调用所有的虚方法。
4、invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
5、invokedynamic:先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法,以上4条指令,分派逻辑是固化在java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。