Bytecode & javap
Stack-based instructions, invokevirtual/invokestatic/invokeinterface/invokedynamic.
a + b work in bytecode?A stack machine
Bytecode instructions push values onto an operand stack and pop them to compute. javap -c disassembles a .class file so you can see them. Here's int add(int a, int b):
iload_1 // push a
iload_2 // push b
iadd // pop both, push a+b
ireturn // return top of stackSlot 0 is this
Locals live in numbered slots. In an instance method, slot 0 holds **this** (a hidden first parameter), so a and b are in slots 1 and 2. That's why the code above starts at iload_1.
And for a static method?
How does static int add(int a, int b) load a?
iload_0iload_1aload_0
Show the answer
**iload_0**. Static methods have no this, so a is in slot 0 and b in slot 1. aload_0 would load a reference, which a isn't.
Five ways to call
invokestatic: static methods like Math.max. invokevirtual: instance methods through a class type. invokeinterface: calls through an interface type. invokespecial: constructors and super calls. invokedynamic: linked at runtime; it creates lambdas.
The variable's type decides
The instruction depends on the static type used for the call. size() through a List variable is invokeinterface; through an ArrayList variable it's invokevirtual. String is a class, so s.length() is invokevirtual too.
List<String> l = new ArrayList<>();
l.size(); // invokeinterface
ArrayList<String> a = new ArrayList<>();
a.size(); // invokevirtualString concatenation
Since Java 9, how does javac compile "Hi " + name + "!" by default?
Think about it, then reveal the answer
As invokedynamic, bootstrapped by **StringConcatFactory** (JEP 280). Before, javac emitted a chain of StringBuilder.append calls. Now the JVM picks the best strategy at runtime, without recompiling your code.
When javap pays off
javap -c -p settles arguments fast: was that constant inlined into the caller? Does this lambda capture this? Which method does this call really bind to? It's also handy for checking which Java version a class was compiled for.
Key takeaways
- The JVM is stack-based, not register-based
- In instance methods, local slot 0 holds this
- invokedynamic: lambdas, string concat (Java 9+), records
- javap -c disassembles a .class file
invokedynamic arrived in Java 7 to help dynamic languages like JRuby on the JVM. javac itself only started emitting it in Java 8, for lambdas.
Practice questions
`javap -c` of the instance method `int add(int a, int b)` shows this. What's in local slot 0?
iload_1
iload_2
iadd
ireturn- this
- The return value
- The Class object
- The parameter a
Check your answer
this. Instance methods receive this as a hidden first parameter in slot 0, so a and b sit in slots 1 and 2.
Since Java 9, how does javac compile `"Hi " + name + "!"` by default?
- A call to String.format
- A chain of StringBuilder.append calls
- invokedynamic, bootstrapped by StringConcatFactory
- invokestatic String.concat
Check your answer
invokedynamic, bootstrapped by StringConcatFactory. JEP 280 switched string concatenation to invokedynamic, letting the JVM choose the best strategy at runtime without recompiling.