How it works
The explanation, with examples you can run.
Stack and heap
A local variable of a primitive type (int, double, boolean) holds the value itself in the frame. A local of a reference type holds only a reference; the object is on the heap. When the method returns, its frame is discarded at once, with no garbage collection involved. If a thread nests too many calls its stack runs out and the JVM throws StackOverflowError; if the heap can't fit a new object even after collecting, it throws OutOfMemoryError.
Java passes every argument by value. For objects, the value is the reference:
public class Refs { static class Point { int x, y; } static void move(Point p, int n) { p.x = 10; // changes the shared object on the heap p = new Point(); // only changes this frame's copy of the reference n = 99; // only changes this frame's copy of the int } public static void main(String[] args) { Point p = new Point(); int n = 1; move(p, n); System.out.println(p.x + " " + n); // 10 1 }}The JIT can blur the picture. If escape analysis proves an object never leaves a compiled method, HotSpot may not allocate it on the heap at all and keep its fields in registers. Your code can't tell the difference.
What an object looks like
On a 64-bit HotSpot JVM, every object starts with a header:
- a mark word (8 bytes) used for the identity hash code, locking and the GC's age bits;
- a class pointer (4 bytes with compressed class pointers, the default) saying what class it is.
Fields follow, arranged by the JVM rather than in source order, and the whole object is padded to a multiple of 8 bytes. Arrays add a 4-byte length. Some sizes this gives:
| Value | Size |
|---|---|
new Object() | 16 bytes |
class Point { int x, y; } | 12 + 8 = 20, padded to 24 |
Integer | 16 bytes, plus a 4-byte reference to it |
int[1000] | 16 + 4000 bytes |
Integer[1000] full of distinct values | 16 + 4000 for the array, plus 16,000 for the boxes |
References are 4 bytes because of compressed oops: HotSpot stores a reference as a 32-bit offset in units of 8 bytes from the start of the heap, which reaches about 32 GB. Set -Xmx above that and references become 8 bytes, so a 33 GB heap can hold less than a 31 GB one.
To see real layouts instead of estimates, use JOL (Java Object Layout) from OpenJDK. And watch JEP 450: in JDK 24 and later, an experimental mode shrinks the header to 8 bytes.
Allocation is cheap
HotSpot gives each thread a private chunk of the young generation, a TLAB (thread-local allocation buffer). Allocating an object is usually a pointer bump inside it, with no lock. The real cost of an object comes later, if it survives long enough for the collector to copy it.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.