Java Heap Memory
Table of Contents + −
In the last lesson you learned the JVM architecture. The JVM splits its memory into areas. The biggest one is the heap, and that is where almost every object you make ends up.
🤔 Where do your objects actually go?
You write new Student() and a student appears. But where does it live? Here is the simple answer:
- Every object you create with
newgoes on the heap. - The heap is the runtime area where all objects and their instance fields live.
- Think of it as a large storage room. A
String, an array, aStudent, aList— if you typednew, it is on the heap. - The variable that names the object does not live there. The object’s data does.
📦 The heap holds everything made with new
This program makes one object and reads a field from it. The object sits on the heap the whole time.
public class Main { static class Student { String name = "Alex"; // an instance field, stored inside the object }
public static void main(String[] args) { Student s = new Student(); // the Student object goes on the HEAP System.out.println(s.name); // read the field from the heap object }}Here is what happens:
new Student()builds aStudentobject and places it on the heap.- The object carries its own fields. The field
nameholds"Alex"inside the object on the heap. - The variable
slives on the stack, insidemain’s frame, not on the heap. sholds a reference, a small value that points to where the object sits.
So the object and its variable are in two places. The object is on the heap. The reference is on the stack.
Output
Alex🔗 References point to heap objects
A reference is like a home address on a slip of paper. The slip is small and fits in your pocket. The house it points to is large and stays put. The slip is the reference; the house is the object.
The heap is shared, so many references can point at the same object. This program shows two references pointing to one array.
public class Main { public static void main(String[] args) { int[] first = {10, 20, 30}; // one array object on the HEAP int[] second = first; // copy the reference, not the array
System.out.println(first == second); // do they point to the same object? }}Here is what each line does:
int[] first = {10, 20, 30}builds one array on the heap and stores its reference infirst.int[] second = firstcopies the reference, not the array. Both variables now hold the same address.first == secondcompares the two addresses. They match, so the result istrue.- One array, two slips of paper carrying the same address.
Output
trueJava copied the reference, not the object. That is the foundation of the next idea.
👥 Aliasing: two names for one object
When two references point to the same object, they are aliases — two names for one thing. Change the object through one alias and the other sees it too. There is only one object.
This program changes a slot through one reference and reads it through the other.
public class Main { public static void main(String[] args) { int[] scores = {1, 2, 3}; int[] alias = scores; // alias and scores point to the same array
alias[0] = 99; // change the array through alias... System.out.println(scores[0]); // ...and scores sees the change
scores[1] = 77; // now change through scores... System.out.println(alias[1]); // ...and alias sees it too }}Let’s trace it:
int[] scores = {1, 2, 3}makes one array;scoresreferences it.int[] alias = scorescopies the reference, so both names point to the one array.alias[0] = 99changes the first slot, andscores[0]reads it back as99.scores[1] = 77changes the second slot, andalias[1]reads it back as77.
Output
9977This is the direct result of references sharing one heap object. Once aliasing clicks, a lot of confusing bugs make sense — two variables you thought were separate were one object all along.
🧵 The heap is shared by all threads
The heap belongs to the whole program. Here is the rule:
- Any thread with a reference can reach any object on the heap.
- The stack is different — each thread gets its own private stack.
- Sharing is what lets two threads work on the same data.
- It is also why two threads changing one object at the same time can cause bugs.
Remember: objects on the heap are shared, references are how each thread reaches them. This program makes one object in the main thread and lets a second thread change it.
public class Main { static class Counter { int value = 0; // instance field on the heap object }
public static void main(String[] args) throws InterruptedException { Counter shared = new Counter(); // one Counter on the HEAP
Thread worker = new Thread(() -> { shared.value = 5; // the new thread changes the heap object });
worker.start(); worker.join(); // wait for the worker to finish
System.out.println(shared.value); // main thread reads the same object }}What is going on:
new Counter()makes one object on the heap; the main thread holds it inshared.- The worker thread gets the same reference, so it points at the very same object.
- The worker sets
shared.value = 5on that shared object. join()waits for the worker, then the main thread reads the same object and sees5.
Output
5♻️ Object lifecycle and garbage collection
You never free memory by hand in Java. The JVM does it. Here is how:
- An object stays alive as long as something still refers to it.
- When nothing can reach it, it becomes eligible for garbage collection.
- Garbage collection is the JVM freeing the memory of objects you no longer use.
- Eligible does not mean freed now. The JVM cleans it up at some later time it decides.
This program drops the only reference to an object, making it eligible.
public class Main { public static void main(String[] args) { String message = new String("hello"); // object on the HEAP, reachable System.out.println(message);
message = null; // drop the only reference; the object is now unreachable // the "hello" object is now eligible for garbage collection }}Let’s read it:
new String("hello")makes aStringon the heap;messagereferences it, so it is reachable.- We print it, which works because it is still alive.
message = nullclears the only reference. Nothing can reach the object now.- From here the object is eligible for collection, and the JVM may free it at any later time.
Output
helloThis is a quick taste. Garbage collection has its own full lesson. The rule: an object lives while it is reachable, and becomes eligible when it is not.
🌱 How the heap is divided inside
The heap is not one flat block. The JVM splits it by how long objects tend to live, which lets the garbage collector work much faster:
- Most objects die young — a method makes one, uses it, drops it.
- A few objects live a long time.
- So the JVM groups new objects together and old objects together, and cleans the young group often.
These are the main parts of the heap:
| Area | What it holds |
|---|---|
| Young generation: Eden | Brand new objects are created here first |
| Young generation: Survivor | Young objects that lived through one cleanup move here |
| Old generation | Objects that have survived many cleanups, the long-lived ones |
The flow: a new object starts in Eden, moves to a Survivor area if it lives through a cleanup, and gets promoted to the Old generation if it keeps surviving. The garbage collection lesson goes deep into how each area is cleaned.
💥 When the heap fills: OutOfMemoryError
The heap is large but not endless. If you keep making objects and holding onto them, the heap fills up, and the JVM throws an OutOfMemoryError when there is no room for a new object.
This program keeps adding arrays to a list forever. The list holds every one, so none can be collected. The heap fills and the program crashes.
import java.util.ArrayList;import java.util.List;
public class Main { public static void main(String[] args) { List<int[]> hoard = new ArrayList<>(); while (true) { hoard.add(new int[1_000_000]); // keep adding big arrays, never let go } }}Why does this run out:
- Each loop makes a big array on the heap and adds it to
hoard. - The list keeps a reference to every array, so every one stays reachable.
- Nothing is eligible for collection, so the heap fills with arrays that can never be freed.
- With no room left, the JVM gives up.
Output
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at Main.main(Main.java:9)The message points right at the trouble: Java heap space. The heap ran out of room, usually because something held onto too many objects.
⚠️ Common Mistakes
Thinking the variable holds the object. It only holds a reference; the object is on the heap.
Student s = new Student();// ❌ wrong: "the whole Student is stored in s"// ✅ right: s holds a reference; the Student object lives on the heapExpecting two references to be independent copies. They share one object, so a change through one shows through the other.
int[] a = {1, 2, 3};int[] b = a;b[0] = 99;// ❌ wrong: "a is untouched because b is a separate copy"// ✅ right: a and b share one array, so a[0] is now 99Thinking an object is freed the instant you stop using it. Setting a reference to null only makes it eligible; the JVM frees it later.
❌ wrong: "message = null deletes the object right away"✅ right: it becomes eligible; the garbage collector frees it at some later time✅ Best Practices
Habits that keep heap memory clear in your code:
- Think in references. Assigning one object variable to another copies the address, not the object.
- Expect shared changes. Two references to one object see each other’s changes.
- Let go of objects you no longer need, so the garbage collector can free their space.
- Watch collections that only grow. A list or map you never clear is the top cause of
OutOfMemoryError. - Read the error name first.
OutOfMemoryError: Java heap spacepoints you at the heap and objects held too long.
🧩 What You’ve Learned
Nicely done. Let’s recap Java heap memory.
- ✅ The heap is the runtime area where all objects created with
new, and their instance fields, live. - ✅ A variable holds a reference that points to its object on the heap. The object’s data is on the heap, not in the variable.
- ✅ The heap is shared by all threads, so any thread with a reference can reach the same object.
- ✅ Two references to one object are aliases. A change through one is seen through the other, because there is only one object.
- ✅ An object lives while it is reachable, and becomes eligible for garbage collection when nothing refers to it.
- ✅ The heap is split into Young (Eden plus Survivor) and Old generations, and a full heap throws
OutOfMemoryError.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
Where are objects created with new stored?
Why: Every object made with new lives on the heap; the variable only holds a reference to it.
- 2
Two references point to the same array. You change a slot through one. What does the other see?
Why: The two references are aliases for one heap object, so a change through one is visible through the other.
- 3
When does an object become eligible for garbage collection?
Why: An object is eligible once it is unreachable; the JVM then frees its heap space at some later time.
- 4
What error happens when the heap fills up with objects that cannot be freed?
Why: A full heap throws OutOfMemoryError: Java heap space, usually because something holds too many live objects.
🚀 What’s Next?
We kept saying that references and local variables live on the stack, not the heap. The stack is the other key memory area, and it explains how method calls and local variables work. Let’s look at it next.