简体   繁体   中英

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.

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: \" .

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.

Refer to the JVM specification :

First, the unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class (§2.6), ... The run-time constant pool entry at the index must be a symbolic reference to a dynamically-computed call site (§5.1).

Conveniently, javap does already look up the constant pool and decode the information; the result is what has been printed like a comment behind the instruction in the line

14: invokedynamic #20,  0             // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;

The number #0 is the index of the BootstrapMethods attribute you have already posted. The meaning of the method name is up to that bootstrap method. Further there's the type descriptor (I)Ljava/lang/String; , so this specific invocation consumes an int and produces a String .

What will happen at runtime depends on the referenced bootstrap method. This invocation refers to the static method StringConcatFactory.makeConcatWithConstants(...) , part of the way, string concatenation is compiled with Java 9.

The documentation of that method tells us that the method name used in the invokedynamic instruction is irrelevant and the static argument of the BootstrapMethod attribute, ie the text + String: \ determines the string format. The \ is the place-holder for “an ordinary argument”, ie the int parameter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM