Dynamic binding in Java happens at run time for overriding functions. I would like to know how it happens internally (like in C++, virtual functions/table is used).
Well as mentioned above, In Java all non-final non-private, not-static methods are virtual, which means any method which can be overridden in a child class is virtual.
But In order to understand how JVM handle it internally let us take below code example
public class OverridingInternalExample {
private static class Mammal {
public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); }
}
private static class Human extends Mammal {
@Override
public void speak() { System.out.println("Hello"); }
// Valid overload of speak
public void speak(String language) {
if (language.equals("Hindi")) System.out.println("Namaste");
else System.out.println("Hello");
}
@Override
public String toString() { return "Human Class"; }
}
// Code below contains the output and and bytecode of the method calls
public static void main(String[] args) {
Mammal anyMammal = new Mammal();
anyMammal.speak(); // Output - ohlllalalalalalaoaoaoa
// 10: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
Mammal humanMammal = new Human();
humanMammal.speak(); // Output - Hello
// 23: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
Human human = new Human();
human.speak(); // Output - Hello
// 36: invokevirtual #7 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:()V
human.speak("Hindi"); // Output - Namaste
// 42: invokevirtual #9 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:(Ljava/lang/String;)V
}
}
We can see bytecode for anyMammal.speak()
and humanMammal.speak()
are same ( invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
) because according to compiler both methods are called on Mammal reference.
So now the question comes if both method calls have same bytecode then how does JVM know which method to call?
Well, the answer is hidden in the bytecode itself and it is invokevirtual
instruction, according to JVM specification
invokevirtual invokes an instance method of an object, dispatching on the (virtual) type of the object. This is the normal method dispatch in the Java programming language.
JVM uses the invokevirtual
instruction to invoke Java equivalent of the C++ virtual methods. In C++ if we want to override one method in another class we need to declare it as virtual
, But in Java, all methods are virtual by default (except final and static methods) because we can override every method in the child class.
Operation invokevirtual
accepts a pointer to method reference call ( #4
an index into the constant pool)
invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
And that method reference #4 again refers to a method name and Class reference
#4 = Methodref #2.#27 // org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
#2 = Class #25 // org/programming/mitra/exercises/OverridingInternalExample$Mammal
#25 = Utf8 org/programming/mitra/exercises/OverridingInternalExample$Mammal
#27 = NameAndType #35:#17 // speak:()V
#35 = Utf8 speak
#17 = Utf8
All these references combinedly used to get a reference to a method and class in which the method is to be found. This is also mentioned in JVM Specification
The Java virtual machine does not mandate any particular internal structure for objects 4.
And the bookmark 4 states
In some of Oracle’s implementations of the Java virtual machine, a reference to a class instance is a pointer to a handle that is itself a pair of pointers: one to a table containing the methods of the object and a pointer to the Class object that represents the type of the object, and the other to the memory allocated from the heap for the object data.
It means every reference variable holds two hidden pointers
But again the question comes, how invokevirtual
internally do this? Well, no one can answer this because it depends on JVM implementation and it varies from JVM to JVM.
And from above statements, we can conclude that an object reference indirectly holds a reference/pointer to a table which holds all the method references of that object. Java has borrowed this concept from C++ and this table is known by various names which such as virtual method table ( VMT ), virtual function table ( vftable ), virtual table ( vtable ), dispatch table.
We can not sure of how vtable
is implemented in Java because it is JVM dependent. But we can expect that it will be following the same strategy as C++ where vtable
is an array like structure which holds method names and their references on array indices. And whenever JVM try to execute a virtual method it always asks the vtable
for it address.
There is only one vtable
per class means it is unique and same for all objects of a class similar to Class
object. I have discussed more on Class
object in my articles Why an outer Java class can’t be static and Why Java is Purely Object-Oriented Language Or Why Not.
So there is only one vtable
for Object
class which contains all 11 methods (if we don't count registerNatives) and references to their respective method bodies.
When JVM loads the Mammal
class into memory it creates a Class
object for it and creates a vtable
which contains all the methods from the vtable of Object class with the same references (Because Mammal
is not overriding any method from Object) and adds a new entry for speak
method.
Now here comes the turn of Human
class and now JVM will copy all entries from the vtable
of Mammal
class to the vtable
of Human
and adds a new entry for the overloaded version of speak(String)
.
JVM knows that Human
class has overridden two methods one is toString()
from Object
and second is speck()
from Mammal
. Now instead of creating new entries for these methods with updated references. JVM will modify the references to the already present methods on the same index where they were present earlier and will keep the same method names.
The invokevirtual
causes the JVM to treat the value at method reference #4
, not as an address but as the name of a method to look up in the vtable
for the current object.
I hope now it would have become a little bit clear that how the JVM mixes constant pool
entries and vtable
to conclude which method it is going to call.
You can read more on my article How Does JVM Handle Method Overloading and Overriding Internally.
All non-final non-private, not-static methods in Java are virtual
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With