周志明先生所著的《深入理解Java虚拟机:JVM高级特性与最佳实践》(购买地址:亚马逊链接),对我学习Java、理解Java之道有非常大的帮助。至今已读过两遍,为了能够融会贯通,加深记忆(人老了记忆力差),便在Blog上记录一些认为该记的东西。
方法调用
方法调用不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。
解析
所有方法调用中的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,虚拟机会将其中的一部分符号引用转化为直接引用,这种解析能成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在程序代码写好、编译器进行编译时就必须确定下来。这类方法的调用称为解析(Resolution)。
在Java中,符合“编译器可知,运行期间不可变”的方法主要有静态方法和私有方法两大类,前者与类型直接关联,后者在外部不可被访问,这两种方法都不可能通过继承或别的方式重写出其他版本,因此它们都适合在类加载阶段进行解析。
可以在解析阶段确定唯一的调用版本的有静态方法、私有方法、实例构造器和父类方法四类,它们在类加载的时候就会把符号引用解析为该方法的直接引用。这些方法可以称为非虚方法。final方法也是非虚方法。
分派
解析调用一定是个静态的过程,在编译期间就完全确定,在类装载的解析阶段就会把涉及的符号引用全部转变为可确定的直接引用,不会延迟到运行期再去完成。而分派(Dispatch)调用则可能是静态的也可能是动态的。
静态分派
/** * 方法静态分派演示 * @author 周志明 */ public class StaticDispatch { static abstract class Human{ } static class Man extends Human{ } static class Women extends Human{ } public void sayHello(Human guy){ System.out.println("hello,guy!"); } public void sayHello(Man guy){ System.out.println("hello,gentleman!"); } public void sayHello(Women guy){ System.out.println("hello,lady!"); } public static void main(String[] args) { Human man=new Man(); Human women=new Women(); StaticDispatch sd=new StaticDispatch(); sd.sayHello(man); sd.sayHello(women); } }
Human man=new Man();
我们把上面代码中的“Human”称为变量的静态类型(Static Type)或者外观类型(Apparent Type),后面的“Man”则称为变量的实际类型(Actual Type)。在main()里面的两次sayHello()方法调用,在方法接收者已经确定是对象“sd”的前提下,使用哪个重载版本,就完全取决于传入参数的数量和数据类型。代码中特意地定义了两个静态类型相同、实际类型不同的变量,但虚拟机(准确地说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据的。静态类型是编译期可知的,所以在编译阶段,Javac编译器就根据参数的静态类型决定使用哪个重载版本,所以选择了sayHello(Human)作为调用目标,并把这个方法的符号引用写到main()方法里的两条invokevirtual指令的参数中。
所有依赖静态类型来定位方法执行版本的分派动作,都称为静态分派。静态分派最典型的应用就是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。(在上面的代码中,如果把sayHello(Human guy)方法删掉,编译器将不能编译该类)。
动态分派
动态分派和重写(Override)有着密切的联系。
/** * 方法动态分派演示 * @author 周志明 */ public class DynamicDispatch { static abstract class Human { protected abstract void sayHello(); } static class Man extends Human { @Override protected void sayHello(){ System.out.println("Man say hello"); } } static class Women extends Human { @Override protected void sayHello(){ System.out.println("Women say hello"); } } public static void main(String[] args) { //以下两句代码首先建立man和women的内存空间,将这两个实例的引用存放在第1和第2个局部变量表Slot之中。 Human man=new Man(); Human women=new Women(); /* 调用该方法前,把man对象的引用压到栈顶,man对象是sayHello()方法的所有者,称为接收者(Receiver) invokevirtual指令调用方法,invokevirtual步骤是: 1、找到操作数栈顶的第一个元素所指向的对象的实际类型(在这里是Man),记作C。 2、如果在类型C中找到与常量中描述符和简单名称都相符的方法(在这里是sayHello()方法),则进行访问权限校验,如果通过则返回这个方法的直接引用,查找结束;不通过则返回异常。 3、否则按照继承关系从下往上一次对C的各个父类进行第2步的过程。 4、如果始终没找到合适的方法,则抛出java.lang.AbstractMethodError异常。 */ man.sayHello(); //调用该方法前,把women对象的引用压到栈顶,……(同上) women.sayHello(); man=new Women(); man.sayHello(); } }
由于invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型,所以两次调用中的invokevirtual指令把常量池中的类方法符号引用解析到了不同的直接引用上,这个过程就是Java语言中方法重写的本质。我们把这种在运行期根据实际类型确定方法执行版本的分派过程成为动态分派。
相关推荐
基于虚拟机字节码注入的Android应用程序隐私保护机制.pdf
深入Java虚拟机JVM类加载学习笔记:jvm java classloader 垃圾回收 gc
基于虚拟机字节码注入的Android应用程序隐私保护机制.docx
详细介绍ASM框架的API、Class文件结构解析、HotSpot虚拟机类加载源码分析、动态代理与字节码插桩的实现。
视频目录 第1节说在前面的话 [免费观看] 00:05:07分钟 | 第2节整个部分要讲的内容说明 [免费观看] 00:06:58分钟 | 第3节环境搭建以及jdk,...第104节字节码执行引擎小结00:03:38分钟 | 第105节总结与回顾00:10:55分钟
使用虚拟机的基本步骤如下: 选择合适的虚拟机软件。市场上有多种虚拟机软件可供选择,如VMware、VirtualBox、Hyper-V等,每种软件都有其特点和优势,用户可以根据自己的需求和硬件环境选择合适的软件
Android虚拟机ART
该文档是本人的学习笔记,尚未精修,后续会精修排版. 学习笔记:深入浅出 Java 虚拟机.docx
第104讲 字节码执行引擎小结 00:03:38 第105讲 总结与回顾 00:10:55 第106讲 happens-before简单概述 00:15:17 第107讲 重排序问题 00:23:19 第108讲 锁的内存语义 00:13:54 第109讲 volatile的...
对于新手来说,如何在忘记Mac虚拟机密码的情况下 修改虚拟机的密码
Java虚拟机(字节码介绍)1
不需要对虚拟机中内核代码进行修改.top: 获得某虚拟机CPU利用率free: 获得某虚拟机内存利用率虚拟机系统调用表保护功能ps: 列出某虚拟机中所有进程信息lsmod: 列出某虚拟机中所有模块(驱动)信息translate: 将某...
Linux虚拟机忘记密码解决方法
在不同的虚拟机实现里,执行引擎在执行 java 代码的时候,可能会解释执行和编译执行等,但是从外观上来看,所有的 java 虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的...
将以太坊虚拟机字节码编译为已编译编程语言(如Java),WIP的源代码。 安装 通过Node上的npm: npm install evm2code 用法 待定 参考 待定 样本 待定 版本号 待定 贡献 随时并提交-欢迎捐款。 如果您提交拉取请求...
虚拟机注册码密匙,虚拟机10.0
C程序学习第一天学习笔记:使用虚拟机基础编译,进制转换,注意事项。
Java 虚拟机学习笔记: Java 内存区域, 垃圾收集, 内存分配与回收策略, JVM 调优, 文件结构, 类加载机制, Java 程序 Java是一种面向对象的编程语言,由Sun Microsystems于1995年推出。它是一种跨平台的语言,...
因为修改虚拟机密码导致sharepoint网页http://demo打不开的解决办法
字节代码的组织形式:8 bipush 100 // Push int constant 1008:当前命令的地址偏移量(8是经过编译器优化后的,易于人使用