全部Java虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果;java
方法调用局部变量表局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序编译为Class文件的时候,就在方法的Code属性的max_locals数据项中肯定了该方法所须要分配的局部变量表的最大容量;局部变量表是以Slot(容量槽)为最小单位;一个Slot能够存放一个32位之内的数据类型;Java中占用32位之内的数据类型有boolean、byte、char、short、int、float、reference和returnAddress8中数据类型;Java语言中明确的64位数据类型只有long和double两种。须要用两个连续的Slot来存放。因为局部变量表创建在线程的堆栈上,是线程私有的数据,不管读写两个连续的Slot是否为原子操做,都不会引发数据安全问题。虚拟机经过索引定位的方式使用局部变量表,索引值的范围从0开始至局部变量表最大的Slot数量。为了尽量节省栈帧空间,局部变量表中的Slot是能够重用的。若是访问的是32位数据类型的变量,索引n就是表明了使用第n个Slot,若是访问的是64位数据类型变量,则说明会同时使用n和n+1两个Slot;类变量有两次赋初始值的过程,一次是在准备阶段,赋予系统初始值;另一次是在初始化阶段,赋予程序员定义的初始值;即便在初始化阶段程序员没有为类变量赋值也没有关系,类变量仍然具备一个肯定的初始值。操做数栈操做数栈也常称为操做栈,它是一个后入先出栈。操做数栈的最大深度在编译的时候就写入方法表的Code属性的max_statcks数据项中;操做数栈的每个元素能够是任意的Java数据类型,包括long和double,32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。当一个方法刚刚开始执行的时候,这个方法的操做数栈是空的,在方法的执行过程当中,会有各类字节码指令往操做数栈中写入和提取内容,也就是出栈/入栈操做。Java虚拟机的解释执行引擎称为“基于栈的执行引擎”,其中所指的“栈”就是操做数栈。动态链接每一个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程当中的动态链接。方法返回地址当一个方法开始执行后,只有两种方式能够退出这个方法:一、执行引擎遇到任意个方法返回的字节码指令,这种退出方法的方式称为正常完成出口。二、在方法执行过程当中遇到异常,而且这个异常没有在方法体内获得处理,这种退出方法的方式称为异常完成出口。
解析类加载的解析阶段,会将其中的一部分符号引用转化为直接引用。解析能成立的前提是:方法在程序真正运行以前就有一个能够肯定的调用版本,而且这个方法的调用版本在运行期是不可改变的。调用目标在程序代码写好、编译器进行编译时就必须肯定下来。在Java语言中符合“编译器可知,运行期不可变”这个要求的方法,主要包括静态方法和私有方法两大类;前者与类型直接关联,后者在外部不可被访问,这两种方法各自的特色决定了他们都不可能经过继承或别的方式重写其余版本,所以它们都适合在类加载阶段进行解析;在Java虚拟机里面提供了5条方法调用字节码指令,以下:
基于栈的字节码解释执行引擎一、invokestatic:调用静态方法;二、invokespecial:调用实例构造器<init>方法、私有方法和父类方法;三、invokevirtual:调用全部虚方法;四、invokeinterface:调用接口方法,会在运行时再肯定一个实现此接口的对象五、invokedynamic:如今运行时动态解析出调用点限定符所引用的方法,而后再执行该方法,在此以前的4条调用指令,分派逻辑是固化在Java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。只要能被invokestatic和invokespecial指令调用的方法,均可以在解析阶段中肯定惟一的调用版本,符合这个条件的有静态方法、私有方法、实力构造器、父类方法4类,它们在类加载的时候就会把符号引用解析为该方法的直接引用。这些方法称为非虚方法。分派静态分派,全部依赖静态类型来定位方法执行版本的分派动做;发生在编译阶段,所以肯定静态分派的动做实际上不是由虚拟机来执行的。例如重载,虚拟机在重载的时候是经过参数的静态类型而不是实例类型做为判断依据的,而且静态类型是编译器可知的。动态分派,在运行期根据实际类型肯定方法执行版本的分派工做;例如重写单分派与多分派,方法的接收者与方法的参数统称为方法的宗量,根据分派基于多少种宗量,能够将分派划分为单分派和多分派;单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个宗量对目标方法进行执行;动态类型语言支持动态类型语言静态类型语言:在编译期肯定类型,最显著的好处是编译器能够提供严谨的类型检查,这样与类型相关的问题能在编码的时候及时发现,利于稳定性及代码达到更大规模;动态类型语言:在运行期肯定类型,这能够为开发人员提供更大的灵活性,某些在静态类型语言中须要大量臃肿代码来实现的功能,由动态类型语言来实现能够更加清晰和简洁,清晰和简洁一般也就意味着开发效率的提高。JDK1.7与动态类型目前已经有许多动态类型语言运行于Java虚拟机之上了,如Clojure、Groovy、Jython和JRuby等。方法符号引用在编译时产生,而动态类型语言只有在运行期才能肯定接收者类型。这样,在Java虚拟机上实现的动态类型语言就不得不使用其余方式实现,这样势必让动态类型语言实现的复杂度增长,也可能带来额外的性能或者内存开销。java.lang.invoke包主要是在以前单纯依靠符号引用来肯定调用的目标方法这种方式之外,提供一种新的动态肯定目标方法的机制,称为MethodHandle。仅站在Java语言的角度来看,MethodHandle的使用方法和效果与Reflection有众多类似之处,不过它们还有如下区别:一、从本质上将,Reflection和MethodHandle机制都是在模拟方法调用,但Reflection是在模拟Java代码层次的方法调用,MethodHandle是在模拟字节码层次的方法调用。二、Reflection中的java.lang.reflect.Method对象远比MethodHandle机制中的java.lang.invoke.MethosHandle对象所包含的信息多。前者是方法在Java一端的全面映像,包含了方法的签名、描述符以及方法属性表中各类属性的java端表现方式,还包含执行权限等的运行期信息。后者仅仅包含与执行该方法有关的信息。Reflection是重量级,MethodHandle是轻量级三、MethodHandle是对字节码的方法指令调用的模拟,因此理论上虚拟机在这方面作的各类优化Reflection API的设计目标只是为了Java语言服务的MethodHandle 则设计成可服务于全部虚拟机之上的语言,其中也包含java语言。invokedynamic指令在某种程度上,invokedynamic指令与MethodHandle机制的做用是同样的,都是为了解决原有4条"invoke*"指令方法分派规则固化在虚拟机之中的问题,把如何查找目标方法的决定权从虚拟机转嫁到具体用户代码中,让用户有更高的自由度。