None of the distinctions in the bytecode will matter much for performance of JIT-compiled code.
HotSpot will routinely keep a type profile for each invokevirtual
call site. If the record indicates only one type being dispatched on, the JIT compiler will treat it as an invokespecial
call, basically a direct jump to the callee, or even inlining of the callee.
The above describes the optimization of the hardest case, a fully general virtual method. HotSpot also knows which methods are effectively final: in the set of loaded classes there is no occurrence of an override for that method. In that case HotSpot proceeds similar to above, but with a few instructions omitted (those which perform the type assertion).
I have additionally put some effort into hacking the .class file such that an invokevirtual
instruction was replaced by invokespecial
. The result was a VerifyError
:
Exception in thread "main" java.lang.VerifyError:
Bad invokespecial instruction: current class isn't assignable to reference class.
You can use invokespecial
only for methods of the current class or its ancestor, and this is in fact specified by the Java Virtual Machine Specification, §4.9.2:
Each
invokespecial
instruction must name an instance initialization method (§2.9), a method in the current class, or a method in a superclass of the current class.