Memory Areas

JVM Runtime Data Areas

┌──────────────────────── shared by all threads ─────────────────────────┐
│  HEAP: all objects and arrays                     METASPACE (native)   │
│  ┌───────────────── Young ───────────────┐ ┌──────┐  class metadata,   │
│  │  Eden  │ Survivor S0 │ Survivor S1    │ │ Old  │  method bytecode,  │
│  └───────────────────────────────────────┘ └──────┘  static fields*    │
└────────────────────────────────────────────────────────────────────────┘
┌──────── per thread ────────┐  ┌──────── per thread ────────┐
│ JVM STACK: one frame per   │  │ PC register, native stack  │
│ method call (locals, args, │  └────────────────────────────┘
│ operand stack)             │
└────────────────────────────┘     * static field values live with the Class object on the heap
AreaHoldsError when fullSize flag
Heapobjects, arrays, String poolOutOfMemoryError: Java heap space-Xms, -Xmx
Stackframes: local variables, references, return addressStackOverflowError-Xss
Metaspaceclass metadata (replaced PermGen in Java 8)OutOfMemoryError: Metaspace-XX:MaxMetaspaceSize
Code cacheJIT-compiled native codeJIT stops compiling-XX:ReservedCodeCacheSize

Stack vs Heap in Code

Example

Local variables and references live on the stack; the objects they point to live on the heap. Java passes the reference by value: a method can change the object but not the caller's variable.

public class Main {
    public static void main(String[] args) {
        int qty = 1;                         // primitive value on main's stack frame
        Cart cart = new Cart();              // reference on the stack, Cart object on the heap
        update(qty, cart);
        System.out.println("qty after call:   " + qty);
        System.out.println("items after call: " + cart.items);

        reassign(cart);
        System.out.println("items after reassign: " + cart.items);
    }

    static void update(int q, Cart c) {      // new frame: copies of qty and of the reference
        q = 99;                              // changes only the copy
        c.items++;                           // changes the shared heap object
    }

    static void reassign(Cart c) {
        c = new Cart();                      // the caller's reference is untouched
        c.items = 500;
    }
}

class Cart { int items = 0; }

Output

qty after call:   1
items after call: 1
items after reassign: 1

StackOverflowError

Example

Every call adds a frame. Infinite recursion fills the thread stack.

public class Main {
    static int depth = 0;
    static void recurse() { depth++; recurse(); }

    public static void main(String[] args) {
        try {
            recurse();
        } catch (StackOverflowError e) {
            System.out.println("Caught StackOverflowError after more than 1000 frames: " + (depth > 1000));
        }
    }
}

Output

Caught StackOverflowError after more than 1000 frames: true