Short answer
The heap and Metaspace are shared across all threads, while each thread gets its own JVM stack, PC register, and native stack.
Explanation
That is precisely why heap data needs synchronization and the memory model, but local variables and the call stack are inherently thread-safe and never escape a thread unless you publish them. It also explains thread cost: each platform thread reserves stack memory (often ~1 MB by -Xss), so tens of thousands of threads exhaust native memory long before the heap does.
Common mistakes
- Saying the heap is per-thread.
- Forgetting the PC register and native method stack are per-thread too.