(1)字节码指令-cinit
构造方法可以分为两类,一类是cinit 一类init
cinit是整个类的构造方法
putstatic:进行static变量的赋值,是到常量池里找到名字一个叫做i的变量
(2)字节码指令-init
init是:每个构造对象的构造方法
对成员变量的赋值,初始化代码块,构造方法里面的代码,最终呢也也会收集成同一个方法,会把这些代码最终呢合并在一起
以上呢就是对整个构造方法,关联到成员变量赋值操作,初始化代码块以后的执行的流程
(3)方法的调用
我们来看看不同的方法他们调用的字节码指令是否一样
如果是构造方法调用的是invokespecial 私有也是调用 invokespecial final也是调用的invokespecial 普通的public调用的invokevirtual 两次静态方法调用的invokestatic
public的方法是不能唯一确定的,它可能出现方法重写的情况,因此在编译期间他不能唯一确定它调用的是那个方法,也许是子类的也许是父类的invokevirtual 所以称之为动态绑定,需要在运行的时候确定
在性能上invokespecial 和invokestatic性能更高一些因为他属于静态绑定,将来就能找到方法的执行地址了,invokevirtual需要确定多次才能确定方法的执行地址这是这几个方法调用的指令
pop是静态方法的调用是不需要对象的pop把对象出栈
(4)多态原理-HSDB
invokevirtual是显现方法的多态调用,它的工作方式相对较为复杂,下面研究一下执行流程
使用虚拟机工具可以看到我们虚拟机中比较底层的内存状态和内存地址
(5)多态原理-查找类
查询Dog这个对象:查询出对象在内存中的地址
这个对象呢分了对象头,成员变量,这里质包含了对象头16个字节:前8个字节mark1:包含对象的哈希吗以及后来加锁时的锁标记,后面8个字节是对象的类型指针,根据对象的类型指针可以找到Class类
可以查看这俩内存的实际地址:
对象类型 指针的类型表示:Dog在java虚拟机里的样子,它是处在方法区包含了这个类的所有信息
常量池的指针地址,方法入口的指针地址
(6)多态原理-vtable
下面我们来看类中的方法指那种多态的方法它是存在一种交vtable的虚方法表中,前面提到的静态方法,final方法,私有方法,他们不会列在这个虚方法表中,他在类的结构的中的最后一个
vtable的地址是:028换成1E0
在对象头里可以看到vtable的长度:
虚方法表中最后一个方法的地址就是对应Dog类的eat方法
虚方法表中第三项实际调用父类Animal中的toString方法
Object类:
我们对对象的虚方法表做了一个分析,以后呢通过对象找到Class类,通过Class类知道虚方法表后,我们可以确定每个方法的实际入口地址发现有的来自父类,有的是本类,将来我们就知道一个对象调用方法时到底找的是那个方法,虚方法表是在类的加载过程中的连接阶段就会生成虚方法表,在链接的时候确定了每一个方法的入口地址
(7)多态原理-总结
这个是多态调用时方法的查找过程,这个过程还是比较复杂的,需要查找很多步,从细微的效率上不如:invokespecial 和invokestatic,因为它设计到运行期间动态的对它进行查找,当然JVM也会对这个查找做一个进一步优化,有一个缓存,当反复的调用同一个方法达到次数以后,就会从缓存中找到这个方法的字节码地址,就不用经历这么多的查找步骤了
多态在运行期间虚拟机可以做一个单态的优化,也可以加快方法的取值速度