Java虚拟机学习05 | JVM是如何执行方法调用的?(下)

https://time.geekbang.org/column/article/12098

虚方法调用

Java中所有的非私有实例方法调用都会被编译成invokeirtual指令.而接口方法都会被编译成invokeinterface指令,这两种指令都属于Java的虚方法调用

在大多数情况下,Java虚拟机需要根据调用者的动态类型,来确定虚方法调用的目标方法,这个过程我们称为动态绑定. 相对于静态绑定的非虚方法调用来说,虚方法调用更加耗时

在Java虚拟机中静态绑定用户静态方法调用指令invokestatic和调用构造器方法,私有实例方法以及超类非私有实例方法的指令invokespecial. 如果虚方法指向一个final修饰的方法,那么Java虚拟机也可以静态绑定该虚方法调用的目标方法

Java虚拟机采用空间换时间的策略来实现动态绑定. 它为每个类生成一个对应的方法表,用来快速定为目标方法

方法表

方法表本质是一个数组,每个数组元素都指向当前类以及祖先类的非私有实例方法.这些方法可能是具体的,可执行的方法,也可能是没有相应字节码的抽象方法

方法表满足以下两点:一: 子类方法表中包含父类方法表中的所有方法, 二: 子类方法在方法表中的索引值与它重写的父类方法的索引值相同

方法调用指令中的符号引用会在执行之前解析成实际引用,对动态绑定而言,实际引用就是方法表中的索引

实际上,使用了方法表的动态绑定与静态绑定相比,仅仅多出几个内存解引用操作:访问栈上的调用者,读取调用者的动态类型,读取该类型的方法表,读取方法表中某个索引值所对应的目标方法。相对于创建并初始化 Java 栈帧来说,这几个内存解引用操作的开销简直可以忽略不计。

内联缓存

内联缓存是动态绑定的一种效率优化方式. 它能够缓存虚方法调用中调用者的动态类型, 以及该类型所对应的目标方法. 在之后的执行过程中,如果碰到已缓存的类型,内联缓存便会直接调用该类型所对应的目标方法. 如果没有碰到已缓存的类型,内联缓存则会退化至使用基于方法表的动态绑定.

内联缓存分为单态内联缓存,多态内联缓存,超多态内联缓存

  • 单态内联缓存:便是只缓存了一种动态类型以及它所对应的目标方法. 它的实现非常简单:比较所缓存的动态类型,如果命中,则直接调用对应的目标方法.
  • 多态内联缓存:缓存了多个动态类型及其目标方法. 它需要逐个将所缓存的动态类型与当前动态类型进行比较,如果命中,则调用对应的目标方法.
  • 超多态内联缓存:

当内联缓存没有命中的情况下,有以下两种方法:

  1. 替换单态内联缓存中的记录
  2. 劣化为超多态状态

猜你喜欢

转载自blog.csdn.net/qq_34332035/article/details/87254084