⚙️ JVM Internals & Memory · Advanced

Bytecode & javap

Stack-based instructions, invokevirtual/invokestatic/invokeinterface/invokedynamic.

🧩 The mysteryYour CPU has registers; the JVM's instruction set has none. So how does 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 stack

Slot 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.

🔮 Predict it

And for a static method?

How does static int add(int a, int b) load a?

  1. iload_0
  2. iload_1
  3. aload_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 trap

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();     // invokevirtual
🤔 Think first

String 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.

💼 In the real world

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

  1. The JVM is stack-based, not register-based
  2. In instance methods, local slot 0 holds this
  3. invokedynamic: lambdas, string concat (Java 9+), records
  4. javap -c disassembles a .class file
🤯 Did you know?

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
  1. this
  2. The return value
  3. The Class object
  4. 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?

  1. A call to String.format
  2. A chain of StringBuilder.append calls
  3. invokedynamic, bootstrapped by StringConcatFactory
  4. 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.

You can't edit the bytecode of a running app, but you can tune the JVM that runs it. Next: -Xmx, -Xss and friends.