Reputation: 7162
I have the following java code:
class Father {
public void walk(int x) { System.out.format("Fwalk %d", x); }
public void run (int x) { System.out.format("Frun %d", x); }
public void swim(int x) { System.out.format("Fswim %d", x); }
}
class Son extends Father {
public void swim(int x) { System.out.format("Fswim %d", x); }
public void cook(int x) { System.out.format("Scook %d", x); }
public void run (int x) { System.out.format("Frun %d", x); }
}
class main {
public static void main(String[] args) {
Father f = new Father();
f.run(1);
f.swim(2);
Son s = new Son();
s.run(3);
s.swim(4);
}
}
I'm looking for the actual offsets of swim
and run
in the virtual function tables. When I use an equivalent C++ code, I am able to do that easily with objdump:
663 f->run(1);
664 8c3: 48 8b 45 e0 mov -0x20(%rbp),%rax
665 8c7: 48 8b 00 mov (%rax),%rax
666 8ca: 48 83 c0 08 add $0x8,%rax <------ offset 8 for father::run
And the same offset for inherited class:
689 s->run(3);
690 914: 48 8b 45 e8 mov -0x18(%rbp),%rax
691 918: 48 8b 00 mov (%rax),%rax
692 91b: 48 83 c0 08 add $0x8,%rax <------ offset 8 for son::run
When I use Apache's class utility, I get something close (the pool constants)
1)CONSTANT_Methodref[10](class_index = 11, name_and_type_index = 20)
2)CONSTANT_Class[7](name_index = 21)
3)CONSTANT_Methodref[10](class_index = 2, name_and_type_index = 20)
4)CONSTANT_Methodref[10](class_index = 2, name_and_type_index = 22)
5)CONSTANT_Methodref[10](class_index = 2, name_and_type_index = 23)
6)CONSTANT_Class[7](name_index = 24)
7)CONSTANT_Methodref[10](class_index = 6, name_and_type_index = 20)
8)CONSTANT_Methodref[10](class_index = 6, name_and_type_index = 22)
9)CONSTANT_Methodref[10](class_index = 6, name_and_type_index = 23)
10)CONSTANT_Class[7](name_index = 16)
11)CONSTANT_Class[7](name_index = 25)
12)CONSTANT_Utf8[1]("<init>")
13)CONSTANT_Utf8[1]("()V")
14)CONSTANT_Utf8[1]("Code")
15)CONSTANT_Utf8[1]("LineNumberTable")
16)CONSTANT_Utf8[1]("main")
17)CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
18)CONSTANT_Utf8[1]("SourceFile")
19)CONSTANT_Utf8[1]("main.java")
20)CONSTANT_NameAndType[12](name_index = 12, signature_index = 13)
21)CONSTANT_Utf8[1]("Father")
22)CONSTANT_NameAndType[12](name_index = 26, signature_index = 27)
23)CONSTANT_NameAndType[12](name_index = 28, signature_index = 27)
24)CONSTANT_Utf8[1]("Son")
25)CONSTANT_Utf8[1]("java/lang/Object")
26)CONSTANT_Utf8[1]("run")
27)CONSTANT_Utf8[1]("(I)V")
28)CONSTANT_Utf8[1]("swim")
It seems like the entries to the virtual function table are name-based ("run", "swim"). Is this really so? For completion, here is the Java script to extract the constants pool.
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.ConstantPool;
class test
{
public static void main(String[] args)
{
try
{
ClassParser c = new ClassParser(args[0]);
JavaClass j = c.parse();
ConstantPool cp = j.getConstantPool();
System.out.format(cp.toString());
}
catch (Exception e) { return; }
}
}
Upvotes: 2
Views: 361
Reputation: 319
Post from Lalith Suresh [3]
At the bytecode level, the invokevirtual operation is what triggers the Java equivalent of a C++ virtual method call [1]. As per the specification, the operation accepts an index (two actually, but it is combined to get a single value) which is used to get a reference to a method and a reference to the class in which the method is to be found. In HotSpot [2], this corresponds to a vtable index within the target class, resolving which the intended target method can be invoked. Depending on the class hierarchy, each class may have a differently sized vtable, and child classes that override methods of a parent can re-use vtable.
However, in the JIT compiled code, the vtable calling sequence is typically not used. HotSpot performs inline caching, wherein the code optimistically assumes that the target of a virtual method always points to the same implementation (meaning the call site is monomorphic). In this case, a check is inserted into the code to verify whether the assumption actually holds (that is, the target hasn't changed), and then control is passed directly to the target method without involving any vtable lookups, akin to how a so-called 'unlinked' call site works. Loosely put, if this assumptions fails enough times, the code falls back to the long-winded vtable lookup approach.
[1] https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokevirtual
[2] https://wikis.oracle.com/display/HotSpotInternals/VirtualCalls
[3] https://www.quora.com/How-is-the-virtual-method-table-implemented-in-Java
Upvotes: 3