PolarSPARC |
The JVM 'Invoke...' Instructions
Bhaskar S | 08/31/2024 |
If one decompiled a Java class and looked under-the-hood, one would notice that the Java Virtual Machine (or JVM for short) uses FIVE different types of method invocation instructions.
In this article, we will explore the situations in which the 5 different JVM method invocation instructions are used via simple code examples.
Before we get started, we will cover two aspects - a concept and an utility.
First, let us understand the concept of a Constant Pool. It is an in-memory, runtime data structure representation of a Java class file, which behaves like a key-value lookup table that contains references to symbols generated by compiler. In other words, it contains references to the names of classes used, initial values of strings and numeric constants, methods and their parameters, and other miscellaneous information that are important for the proper execution of the Java class.
Next, to disessemble a Java class and reveal the bytecode instructions, one would use the javap utility as follows:
$ javap -c -v <full-path-to-the-class>
where the option -c implies disassemble.
With that out off the way, let us now proceed to explore and understand each of the JVM invocation instructions.
invokestatic |
Given a Java class with static method(s), they belong to the specific class and can be invoked directly on the class. Hence, they are often referred to as class method(s).
The invokestatic JVM instruction is used to invoke class method(s) and does not require an class instance reference on which to invoke the specific method. This JVM instruction references an index location in the Constant Pool, which in turn points to the specific method descriptor (fully qualified method name, parameters, and return type) to be called.
Let us consider the following simple Java class:
/* * Name: Sample_1 * Author: Bhaskar S * Date: 08/25/2024 * Blog: https://www.polarsparc.com */ package com.polarsparc.calls; public class Sample_1 { public static void main(String[] args) { Sample_1.call(); } public static void call() {} }
Disassembling the above Java class results in the following trimmed bytecode:
Notice the invokestatic instruction is referring to index #7 in the Constant Pool which is the method reference for the specific method call.
invokespecial |
The invokespecial JVM instruction is used to execute a method based on the type of the object reference rather than the class object. This JVM instruction comes into play in three scenarios, which are as follows:
Class instance initialization
Explicit calls to parent methods using the super keyword
Invoking private methods
Let us consider the following simple Java class:
/* * Name: Sample_2 * Author: Bhaskar S * Date: 08/25/2024 * Blog: https://www.polarsparc.com */ package com.polarsparc.calls; public class Sample_2 { public static void main(String[] args) { new C1(); } static class C1 { } }
Disassembling the above Java class results in the following trimmed bytecode:
Notice the invokestatic instruction is referring to index #7 in the Constant Pool which is the method reference for the specific method call.
invokevirtual |
The invokevirtual is the default JVM instruction to dynamically invoke a non-static, class instance method (based on the exact name and type) on an object reference. The specified method is invoked on the specific object reference at the top of the operand stack, at runtime, thus allowing support for the polymorphic behavior.
Let us consider the following simple Java class:
/* * Name: Sample_3 * Author: Bhaskar S * Date: 08/25/2024 * Blog: https://www.polarsparc.com */ package com.polarsparc.calls; public class Sample_3 { public static void main(String[] args) { C1 c1 = new C2(); c1.call(); } static class C1 { void call() {} } static class C2 extends C1 { @Override void call() {} } }
Disassembling the above Java class results in the following trimmed bytecode:
Notice the invokespecial instruction is referring to index #9 in the Constant Pool which is constructor call for the class C2, which is the this object reference at the top of the stack frame.
invokeinterface |
The invokeinterface is similar to the invokevirtual JVM instruction except that it is used when the object reference is an interface type.
Let us consider the following simple Java class:
/* * Name: Sample_4 * Author: Bhaskar S * Date: 08/25/2024 * Blog: https://www.polarsparc.com */ package com.polarsparc.calls; public class Sample_4 { public static void main(String[] args) { C1 c1 = new C2(); c1.call(); } static interface C1 { public void call(); } static class C2 implements C1 { @Override public void call() {} } }
Disassembling the above Java class results in the following trimmed bytecode:
Notice the invokeinterface instruction is referring to index #10 in the Constant Pool which is a method reference to an interface type.
invokedynamic |
Before we proceed further, we need to review the following three concepts:
Call Site
Method Handle
Bootstrap Method
A Call Site (implemented as the class java.lang.invoke.CallSite) is the location in the bytecode where a method invocation instruction occurs.
A Method Handle (implemented as the class java.lang.invoke.MethodHandle) is the Java language equivalent of a function pointer that is type-safe.
A Bootstrap Method is a special method that helps resolve the target method to be called at runtime. It returns a reference to a CallSite with the correct target method binding.
The invokedynamic JVM instruction is used for supporting dynamic languages (such as Groovy, JPython, etc) where the method binding happens much later at runtime (duck typing), thus allowing the JVM to optimize the dynamic method invocation.
When the JVM encounters this instruction, it uses the operands to lookup the target CallSite. If the CallSite is not linked to any method, the JVM invokes the Bootstrap method to resolve the method binding. Once resolved, the CallSite points to a valid MethodHandle which allows the JVM to invoke the appropriate target method.
Let us consider the following simple Java class:
/* * Name: Sample_5 * Author: Bhaskar S * Date: 08/25/2024 * Blog: https://www.polarsparc.com */ package com.polarsparc.calls; public class Sample_5 { public static void main(String[] args) { Runnable r = () -> {}; r.run(); } }
Disassembling the above Java class results in the following trimmed bytecode:
Notice the invokedynamic instruction in the Constant Pool is referring to index #0 of the BootstrapMethods section, which in turn resolves to a lambda target in the Constant Pool at index #36.
References