How does method dispatch happen in Java?

 Have you ever wondered when you call a method like a list.add("Shamik"), How the actual method invoke in runtime?
If you want to discover the How part then you are in the right place else you can easily skip the article as it is not related to coding perspective. we know in Java we maintain two steps process
Compiler compiles and make the bytecodes
The interpreter takes bytecode and changes the instruction to machine code.
But think, When your code compiles to bytecode how the method call looks like and in the runtime how the dynamic linking happens, In a simple word how JVM find the actual method and call that method.
In this tutorial, we will discuss the same.
In java(till Java7) we have four types of method

1. Static methods.
2. private, package private or public methods
3. Interface methods declaration.
4. Some special methods like the constructor, super etc.

Now, as the actual method call happens at runtime, somehow at compile time(bytecode) we have to instruct JVM where to find the method or location of the method. But for some cases it is not possible to tell earlier(compile time) which method will be invoked( like in case of overriding, Polymorphism) so compiler has to defer the lookup of the method until runtime, so there are different types of opcodes are used by compiler to tell JVM what to do in runtime.
At runtime, JVM maintains a runtime table called vtable where each entry represents the precise location of the method. The help of this vtable, JVM actually dispatches the call to an actual method.
opcodes
In bytecode, java uses 4 opcodes still java6 but in java7 there is a new opcode introduced called invokedynamic, I will write a separate article on invokedynamic opcode but in this article, we will discuss the other opcodes.
invokestatic: invokestatic opcode is used at compile time to dispatch static methods.
invokevirtual: invokevirtual used to dispatch instance methods.
invokespecial: invokespecial is used to dispatch special methods like constructor or super or for the private method.
invokeinterface: invokeinterface is used to dispatch a method call via an interface.
Now, we will write a java example and try to see the bytecode representation of that example.

package com.example.methodcall;

import java.util.ArrayList;
import java.util.List;

public class MethodCall {

public void addCity() {
List<String> city = new ArrayList<String>();
city.add("Kolkata");

}


public void addState() {
ArrayList<String> state = new ArrayList<String>();
state.add("WestBengal");

}

public static void main(String[] args) {
MethodCall target = new MethodCall();
target.addCity();
target.addState();
}

}




Now I want to see the bytecode representation of the above java program so I will run the following command 

javap -c MethodCall.class 


Bytecode will look like following

 public class com.example.methodcall.MethodCall {
  public com.example.methodcall.MethodCall();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public void addCity();
    Code:
       0: new           #15                 // class java/util/ArrayList
       3: dup
       4: invokespecial #17                 // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #18                 // String Kolkata
      11: invokeinterface #20,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: return

  public void addState();
    Code:
       0: new           #15                 // class java/util/ArrayList
       3: dup
       4: invokespecial #17                 // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #31                 // String WestBengal
      11: invokevirtual #33                 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
      14: pop
      15: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class com/example/methodcall/MethodCall
       3: dup
       4: invokespecial #39                 // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #40                 // Method addCity:()V
      12: aload_1
      13: invokevirtual #42                 // Method addState:()V
      16: return
}


Deep dive into the bytecode Representation :
In the above bytecode representation, except invokestatic all opcodes has been used.
If you noticed the bytecode minutely you can explore that for each method a section is entitled and each java line converted to a command. Let go through each method section

com.example.methodcall.MethodCall(): This is the constructor of MethodCall class, here you can find an invokespecial call because this opcode is used for calling a special method like constructor or super etc. if you pay attention to the commented line beside the invokespecial call you will find the method details
// Method java/lang/Object."<init>":() V: This says constructor can be found in java.lang.object which is detonated by a special symbol <init> and it takes nothing as an argument

 public void addCity() In this section bytecode use invokeinterface opcode for  the line
List<String> city = new ArrayList<String>();
city.add("Kolkata");
and it is commented
as

// InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z which means add is an interface method  which is in java.util.List and it takes Object as an input argument.

Here invokeInterface opcode is used because, as we did the polymorphic assignment so at the compile time there is no way to know where is the actual add method implementation, so compiler has to put such opcode which will instruct JVM to dispatch the call to exact method from Vtable at runtime, so method resolution happens at runtime.

 public void addState():
In this section bytecode use invokevirtual opcode for  the line
ArrayList<String> state = new ArrayList<String>();
state.add("WestBengal");
and it is commented as

// Method java/util/ArrayList.add:(Ljava/lang/Object;)Z which means add can be found in java.util.ArrayList and it takes Object as an input argument.

There, is very subtle difference in coding -- we use ArrayList instead of List so it is not a polymorphic assignment so it creates a huge difference in bytecode now bytecode knows the exact class where to find the add method at compile-time but still call will be dispatched in runtime as if some other class can extend ArrayList. But it uses Invokevirtual opcode which is used for calling an instance method.

public static void main(java.lang.String[]): The last section is entitled to the main method where we call two instance methods addCity and addState so it uses invokevirtual opcode.

Conclusion : In this article we have a fair bit of an idea how method call is happened using different opcodes, But in Java7 an important opcode has been added that is invokeDynamic, which opens the door to allow dynamic type language in JVM, so other languages which run on top of JVM uses this invokeDynamic opcode to make them dynamic language certain extent also Lambda Expression in Java8 uses the invokedynamic opcode, In my next tutorial I will give a detailed overview on -- invokeDynamic opcode.


Post a Comment