A JVM stack frame is a surprisingly mutable data structure that gets rebuilt constantly as your program runs, not a static snapshot.

Let’s watch this in action. Imagine we have a simple Java method:

public class StackDemo {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int sum = add(a, b);
        System.out.println("The sum is: " + sum);
    }

    public static int add(int x, int y) {
        int result = x + y;
        return result;
    }
}

When main starts, a stack frame is pushed onto the JVM’s operand stack for the main method. Within this frame, a "local variables table" is allocated. Initially, this table for main might look something like this (conceptually, indices are 0-based):

  • Index 0: args (a reference to the String array)
  • Index 1: a (the integer 10)
  • Index 2: b (the integer 20)
  • Index 3: sum (initially uninitialized, then holds the integer 30)

When main calls add(a, b), the JVM pushes a new stack frame for the add method. The local variables table for add is created. Crucially, the parameters x and y are placed into specific slots in this new frame’s local variables table before the add method’s code even begins executing.

  • Index 0: x (receives the value of a, which is 10)
  • Index 1: y (receives the value of b, which is 20)
  • Index 2: result (initially uninitialized, then holds the integer 30)

Notice how x occupies index 0, and y occupies index 1. If add had another local variable, say temp, it would occupy index 2, and result would be pushed to index 3. Primitive types and object references each occupy a single slot. Longs and doubles, however, occupy two consecutive slots because they are 64-bit values.

The add method executes. It reads x and y from its local variables table (indices 0 and 1), performs the addition, and stores the result (30) into the result slot (index 2).

When add returns, its stack frame is popped from the JVM’s stack. The result (30) is returned to the caller (main). In main’s stack frame, the value 30 is then placed into the sum slot (index 3).

The key takeaway is that the "local variables table" isn’t just a static list of names. It’s a dynamic array of slots where values are read from and written to by index. The JVM uses these indices to manage the state of each method call. When a method is invoked, a frame is allocated, its local variables table is populated (including parameters), and execution proceeds by manipulating these slots. When the method returns, the frame is discarded, and its local variables are gone.

The concept of local variable tables is fundamental to understanding how the JVM executes code, manages method calls, and handles data. It’s how variables are scoped and how their values are passed between methods. Without this mechanism, the entire call stack model would collapse.

The next thing you’ll bump into is how the JVM optimizes these local variable tables, especially with variable scoping and reuse of slots.

Want structured learning?

Take the full Jvm course →