问题描述
JVM可以记住某个方法在方法表的位置 ,从而下次执行invokevirtual时就不需要搜索方法表了。但是invokeinterface无法确定出方法在方法表中的位置,那么hotspot的实现有没有用其它方法对此进行优化?
如果没有,是不是意味着invokeinterface要比invokevirtual慢呢?
先简短说:HotSpot VM有对invokeinterface做优化。最快的情况下它跟invokevirtual的最快情况一样快;最慢的情况下它慢于invokevirtual最慢的情况。
HotSpot VM实现invokeinterface可以有下面几种层次,从快到慢:
- 完全去虚化(devirtualize)并且完全内联:没有任何虚方法分派或调用开销。在C1或C2编译的代码中使用;
- 条件去虚化(guarded devirtualize)并且内联:有简单的直接类型检查开销,除此之外没有额外的调用开销。这种内联在每个调用点可以内联最多两个目标类型/方法。仅在C2编译的代码中使用;
- 完全去虚化但是没有内联:没有虚方法分派查找目标方法的开销,但是有普通的直接调用开销(所谓直接调用就是类似没有内联时的invokespecial一样,目标是固定的,不用查表)。在C1或C2编译的代码中使用;
- 条件去虚化但是没有内联:有简单的直接类型检查开销,然后有普通的直接调用开销。仅在C2编译的代码中使用;
- 单态内联缓存(monomorphic inline cache)调用:有一个简单的直接类型检查开销,然后是普通的直接调用开销。在C1或C2编译的代码中使用;
- 劣化到超多态(megamorphic)状态的内联缓存(inline cache)调用:这是(5)的劣化情况——当一个内联缓存调用点遇到多于一种实际被调用对象类型就会这样。此时实际上会通过接口方法表(interface method table,简称itable)查找目标方法,然后再调用过去。这是最慢的情况之一,在C1或C2编译的代码中使用;
- 直接通过itable查找目标然后调用:最慢的情况的另一版本。仅在解释器里使用。
上述invokeinterface的7种情况里,头5种都跟invokevirtual一样,都不需要查表,因而这些情况下两者的性能一模一样;最后两种是属于慢速情况,此时由于HotSpot VM的虚方法表(virtual method Table,简称vtable)比itable结构简单,所以invokevirtual会比invokeinterface快一些。
涉及的关键字:
- 去虚化(devirtualization)
- 条件去虚化(guarded devirtualization)
- 类层级分析(class hierarchy analysis,简称CHA)
- 性能分析引导优化(profile guided optimization,简称PGO)
- 数据流分析(data-flow analysis)
- 内联缓存调用(inline cached call)
- 单态内联缓存(monomorphic inline cache)
- 多态内联缓存(polymorphic inline cache):HotSpot VM没有使用这个,不过在讲inline cache的资料里多半会提到它所以这里也把它列举出来。
- 超多态内联缓存(megamorphic inline cache):劣化的inline cache
- 虚方法表(virtual method table,简称vtable)
- 接口方法表(interface method table,简称itable)
题主想知道详细的话我回头再写点。
=================================================
1. 完全去虚化并且完全内联
这主要是通过编译器分析出被调用对象的具体类型,或者通过CHA实现的。其效果是⋯⋯(待续)