Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java JVM bytecode notation, comment grammar. InvokeDynamic

Question: What does line 14 means?

Use javap -v -c to disassembly the following code:

 public class test {
     static int i = 2;
     public static void main(String[] args) {
         test x = new test();
         System.out.println("text + String: " + i);
     } 
 }

in the main function we get the following:

14: invokedynamic #20,  0             // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
19: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
...
BootstrapMethods:
  0: #38 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #44 text + String: \u0001

So, for example, line 19 means that invokevirtual function from the #24 item in the runtime constant pool. The method invoked is println() from the class java/io/PrintStream, its input is from the class Ljava/lang/String, its return value is Void.

As for line 14, #0 holds the reference to the BootstrapMethod and returns an Object whose class is CallSite right? Then:

  1. what is #20 pointing to?
  2. What does the comment #0:makeConcatWithConstants:(I)Ljava/lang/String; means?

Also, where could I find more about the Javap disassembly code's grammar? or what is the right keyword? Oracle's document about the JVM instruction set does not seems to describe clearly about the meaning of the comment.

like image 949
chnnn Avatar asked Jan 26 '23 02:01

chnnn


1 Answers

The short version: Java uses invokedynamic to concatenate strings since Java 9.

Let's break this down a bit:

Invokedynamic has two steps:

  • When the instruction is invoked for the first time, the bootstrap method is called. When it returns, the call site will be linked to the result of the bootstrap method.
  • Subsequent calls will directly call the target MethodHandle.

The CallSite is just a holder for that MethodHandle. Depending on the CallSite subclass used the site might be later relinked.

If we look at the instruction, we see the following at the end:

#0:makeConcatWithConstants:(I)Ljava/lang/String;

The first part (#0) means: Bootstrap method #0.
The second part is the name - which is passed to the bootstrap method and may or may not be used there.
The third part is the method type of the resulting target. In our case: A method that takes an int and returns a java.lang.String.

If we now take a look at the bootstrap method #0 we see a method reference, here to StringConcatFactory.makeConcatWithConstants(...). We also see that there is an additional argument: The String "text + String: \u0001".

The job of the bootstrap method is now to return a MethodHandle (inside a CallSite) which does in this case this string concatenation. But how it does the string concatenation (StringBuilder, String.format, bytecode spinning, chaining MethodHandles...) does not matter for the actual class. It only wants to have Strings concatenated.


Let's try to emulate that behavior by hand. After all, the bootstrap method is an ordinary Java method:

public static void main(String[] args) throws Throwable {
    CallSite cs = StringConcatFactory.makeConcatWithConstants(MethodHandles.lookup(),
            "makeConcatWithConstants", MethodType.methodType(String.class, int.class),
            "text + String: \u0001");

    int x = 2;
    String result = (String) cs.dynamicInvoker().invokeExact(x);
    System.out.println(result);

    x = 3;
    result = (String) cs.dynamicInvoker().invokeExact(x);
    System.out.println(result);
}

(The VM does some more stuff, like it remembers the result and would not call the bootstrap method again, but for our small example this is good enough).


At this point we can take a look under the hood on how the bootstrap method does it's job.
It turns out: You can configure the VM to use different strategies.
And it uses it's privileged position inside java.base to access a package private constructor for java.lang.String that doesn't copying the array - which is safe if the contents is not modified afterwards.

The default strategy is MethodHandle chaining.

The good news is: If someone writes at some point a better strategy, your program will benefit from it - without recompilation.

like image 152
Johannes Kuhn Avatar answered Jan 27 '23 14:01

Johannes Kuhn