java虚拟机基础知识大全 java虚拟机运行原理( 九 )


方法的接收者与方法的参数统称为方法的宗量,这个定义最早应该来源于著名的《Java与模式》一书 。根据分派基于多少种宗量,可以将分派划分为单分派和多分派两种 。单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个宗量对目标方法进行选择 。
根据上述论证的结果,我们可以总结一句:如今(直至本书编写的Java 12和预览版的Java 13)的Java语言是一门静态多分派、动态单分派的语言 。
JDK 7的发布的字节码首位新成员——指令 。
动态类型语言的关键特征是它的类型检查的主体过程是在运行期而不是编译期进行的,满足这个特征的语言有很多,常用的包括:APL、、、、、Lisp、Lua、PHP、、、Ruby、、Tcl,等等 。那相对地,在编译期就进行类型检查过程的语言,譬如C++和Java等就是最常用的静态类型语言 。
Java虚拟机层面对动态类型语言的支持一直都还有所欠缺,主要表现在方法调用方面:JDK 7以前的字节码指令集中,4条方法调用指令(、、、)的第一个参数都是被调用的方法的符号引用(nfo或者常量),前面已经提到过,方法的符号引用在编译时产生,而动态类型语言只有在运行期才能确定方法的接收者 。
java.lang.包[插图]是JSR 292的一个重要组成部分,这个包的主要目的是在之前单纯依靠符号引用来确定调用的目标方法这条路之外,提供一种新的动态确定目标方法的机制,称为“方法句柄”( ) 。
指令与机制的作用是一样的,都是为了解决原有4条“*”指令方法分派规则完全固化在虚拟机之中的问题,把如何查找目标方法的决定权从虚拟机转嫁到具体用户代码之中,让用户(广义的用户,包含其他程序语言的设计者)有更高的自由度 。
读、理解,然后获得执行能力 。大部分的程序代码转换成物理机的目标代码或虚拟机能执行的指令集之前,都需要下图的步骤:
基于栈的指令集与基于寄存器的指令集这两者之间有什么不同呢?举个最简单的例子,分别使用这两种指令集去计算“1+1”的结果,基于栈的指令集会是这样子的:
两条指令连续把两个常量1压入栈后,iadd指令把栈顶的两个值出栈、相加,然后把结果放回栈顶,最后把栈顶的值放到局部变量表的第0个变量槽中 。这种指令流中的指令通常都是不带参数的,使用操作数栈中的数据作为指令的运算输入,指令的运算结果也存储在操作数栈之中 。
而如果用基于寄存器的指令集,那程序可能会是这个样子:
mov指令把EAX寄存器的值设为1,然后add指令再把这个值加1,结果就保存在EAX寄存器里面 。这种二地址指令是x86指令集中的主流,每个指令都包含两个单独的输入参数,依赖于寄存 。
基于栈的指令集主要优点是可移植,因为寄存器由硬件直接提供[插图],程序直接依赖这些硬件寄存器则不可避免地要受到硬件的约束 。
栈架构指令集的主要缺点是理论上执行速度相对来说会稍慢一些,所有主流物理机的指令集都是寄存器架构 。
例如:
a=100;
b=200;
c=300;
(a+b)*c
javap提示这段代码需要深度为2的操作数栈和4个变量槽的局部变量空间
的类加载
OSGi(Open)是OSGi联盟(OSGi )制订的一个基于Java语言的动态模块化规范(在JDK 9引入的JPMS是静态的模块系统)
字节码生成技术应用于:javac,Web服务器中的JSP编译器,编译时织入的AOP框架,还有很常用的动态代理技术,甚至在使用反射的时候虚拟机都有可能会在运行时生成字节码来提高执行速度 。
动态代理中所说的“动态”,是针对使用Java代码实际编写了代理类的“静态”代理而言的,它的优势不在于省去了编写代理类那一点编码工作量,而是实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景之中 。