On this page
Java Memory Model
The Java Memory Model (JMM) defines how threads interact through memory — specifying when writes by one thread are visible to another and what reorderings the JVM and CPU may perform.
Memory Areas Recap
| Area | Scope | Stores | GC |
|---|---|---|---|
| Heap | All threads | Objects, arrays | Yes |
| Stack | Per thread | Local vars, references | No (frame popped) |
| Metaspace | All threads | Class metadata | Yes (class unloading) |
| PC Register | Per thread | Current instruction | No |
public void example() {
int x = 10; // x on stack
String s = "hello"; // reference on stack, object on heap
int[] arr = new int[5]; // reference on stack, array on heap
}
Heap Structure
┌─────────────────────────────────┐
│ Heap │
│ ┌───────────────────────────┐ │
│ │ Young Generation │ │
│ │ ┌───────┐ ┌─────┬─────┐ │ │
│ │ │ Eden │ │ S0 │ S1 │ │ │
│ │ └───────┘ └─────┴─────┘ │ │
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ Old Generation │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
New objects allocate in Eden. After surviving GC cycles, they promote to Old Generation.
Visibility Problem
Without synchronization, one thread’s writes may not be visible to another:
// Thread 1
ready = true;
value = 42;
// Thread 2
if (ready) {
System.out.println(value); // May print 0 due to reordering/caching
}
Happens-Before Rules
The JMM guarantees visibility through happens-before relationships:
- Program order — Each action in a thread happens-before every subsequent action in that thread
- Monitor lock — Unlock happens-before subsequent lock of the same monitor
- volatile — Write to volatile happens-before subsequent read
- Thread start —
Thread.start()happens-before any action in the started thread - Thread join — All actions in a thread happen-before
Thread.join()returns - Transitivity — If A happens-before B and B happens-before C, then A happens-before C
volatile
Ensures visibility and prevents reordering:
private volatile boolean flag = false;
private int value = 0;
// Writer thread
value = 42;
flag = true; // write visible to all threads
// Reader thread
if (flag) {
System.out.println(value); // guaranteed to see 42
}
volatile does not provide atomicity for compound operations (count++).
final Fields
Properly constructed final fields are visible to all threads without synchronization:
public class ImmutablePoint {
private final int x;
private final int y;
public ImmutablePoint(int x, int y) {
this.x = x;
this.y = y;
}
// safe to read x, y from any thread after construction
}
Stack vs Heap — Common Interview Point
public void method() {
int a = 1; // stack
Integer b = 2; // reference stack, Integer object heap (cached -128 to 127)
Person p = new Person("A"); // reference stack, Person object heap
}
OutOfMemoryError Types
| Error | Cause |
|---|---|
Java heap space |
Heap full — too many objects |
Metaspace |
Too many classes loaded |
Unable to create new native thread |
Too many threads |
Direct buffer memory |
Too many direct ByteBuffers |
Best Practices
- Use
volatilefor simple flags and status fields - Use
synchronizedorjava.util.concurrentfor compound operations - Prefer immutable objects to avoid visibility issues
- Do not assume program order implies visibility across threads
- Understand happens-before before writing lock-free code