目录
如果方法在编译期就确定了具体的调用版本,这个版本在运行时是不可改变的。这样的方法称为非虚方法
静态方法、私有方法、final 方法、实例构造器、父类方法都是非虚方法,其它方法则称为虚方法
1. invokestatic 调用静态方法,解析阶段确定唯一方法版本(非虚方法)
2. invokespecial 调用
3. invokevirtual 调用所有虚方法
4 invokeinterface 调用接口方法
5. invokedynamic 动态解析出需要调用的方法,然后执行
前四条指令固化在虚拟机内部,方法的调用执行不可人为干预,而 invokedynamic 指令则支持由用户确定方法版本。其中 invokestatic 指令和 invokespecial 指令调用的方法称为非虚方法,其余的(final修饰的除外)称为虚方法
JVM 字节码指令集一直比较稳定,一直到 Java7 中才增加了一个 invokedynamic 指令,这是 Java 为了实现 [动态类型语言] 支持而做的一种改进
但是在 Java7 中并没有提供直接生成 invokedynamic 指令的方法,需要借助 ASM 这种底层字节码工具来产生 invokedynamic 指令。直到 Java8 的 Lambda 表达式出现,invokedynamic 指令的生成,在 Java 中才有了直接的生成方式
Java7 中增加的动态语言类型支持的本质是对 Java 虚拟机规范的修改,而不是对 Java 语言规则的修改,这一块相对来讲比较复杂,增加了虚拟机中的方法调用,最直接的受益者就是运行在 Java 平台的动态语言的编译器
1. 找到操作数栈顶的第一个元素所执行的对象的实际类型,记作 C
2. 如果在类型 C 中找到与常量中的描述符合简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束。如果不通过,则返回 Java.lang.IllegalAccessError(IllegalAccessError:程序试图访问或修改一个属性或调用一个方法,这个属性或方法,你没有访问权限。一般的,这个会引起编译器异常。这个错误如果发生在运行时,就说明一个类发生了不兼容的改变)
3. 否则,按照继承关系从下往上依次对 C 的各个父类进行第二步的搜索和验证过程
4. 如果始终没有找到合适的方法,则抛出 Java.lang.AbstractMethodError 异常
在面向对象的编程中,会很频繁的使用到动态分派,如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话就可能影响到执行效率。因此,为了提高性能,JVM 采用在类的方法区建立一个虚方法表(virtual method table)(非虚方法不会出现在表中)。使用索引表来代替查找
每个类中都有一个虚方法表,表中存放各个方法的实际入口
虚方法表会在类加载的链接阶段被创建并开始初始化,类的变量初始值准备完成之后,JVM 会把该类的方法表也初始化完毕