Reputation: 137
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:
#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.
Upvotes: 4
Views: 412
Reputation: 15172
The short version: Java uses invokedynamic to concatenate strings since Java 9.
Let's break this down a bit:
Invokedynamic has two steps:
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.
Upvotes: 5
Reputation: 298499
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, i.e. the text + String: \u0001
determines the string format. The \u0001
is the place-holder for “an ordinary argument”, i.e. the int
parameter.
Upvotes: 3