Here is an example of that problem:
private static int iterateIndirect(ByteBuffer buffer) {
int n = buffer.remaining();
while (n > 0 && buffer.get(n - 1) == 0) {
n--;
}
return n;
}
Profiling is:
static ByteBufferTest::iterateIndirect(Ljava/nio/ByteBuffer;)I
interpreter_invocation_count: 2
invocation_counter: 2
backedge_counter: 156672
mdo size: 600 bytes
0 fast_aload_0
1 invokevirtual 49 <java/nio/ByteBuffer.remaining()I>
0 bci: 1 VirtualCallData count(0) nonprofiled_count(0) entries(1)
'java/nio/DirectByteBuffer'(1 1.00)
method_entries(0)
4 istore_1
5 iload_1
6 ifle 25
56 bci: 6 BranchData taken(0) displacement(144)
not taken(96249)
9 fast_aload_0
10 iload_1
11 iconst_1
12 isub
13 invokevirtual 50 <java/nio/ByteBuffer.get(I)B>
88 bci: 13 VirtualCallData trap/ ByteBufferTest::iterateIndirect(class_check recompiled) count(0) nonprofiled_count(0) entries(2)
'java/nio/HeapByteBuffer'(47103 0.49)
'java/nio/DirectByteBuffer'(49151 0.51)
method_entries(0)
16 ifne 25
144 bci: 16 BranchData trap(intrinsic_or_type_checked_inlining recompiled) flags(224) taken(1) displacement(56)
not taken(96254)
19 iinc #1 -1
22 goto 5
176 bci: 22 JumpData taken(96254) displacement(-120)
25 iload_1
26 ireturn
The method is called with either a HeapByteBuffer or a DirectByteBuffer but profiling didn't run long enough to collect both at bci 1. The profiling at bci 1 DirectByteBuffer) is fed to type speculation. Call at bci 13 uses the speculation, inlines DirectByteBuffer::get(). When a HeapByteBuffer is passed to the compiled method, an uncommon trap occurs, the method is recompiled, speculation is used again to inline DirectByteBuffer::get() but because of the trap a virtual call is compiled to cover cases where the speculation fails. Given the profile data at the call, it would be much better to use bimorphic inlining and ignore speculation.