Stack وheap: صورة توضيحية
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
You already know that each method call gets its own local variables (BEG-014 introduced the call stack), and that new creates an object (BEG-020). This module puts both ideas into one picture so you can see what a running Java program holds in memory.
The picture has two areas. The stack is like a desk: one small working area per method call, holding sticky notes (the local variables). The heap is like a warehouse: a big shared space holding the real objects. A sticky note for an object does not contain the object. It contains the shelf address.
Why it matters: almost every confusing Java behaviour in the next lessons (two variables changing together, a method that "changes" its argument, memory that is never freed) becomes obvious once you can draw this picture. Later, JVM tuning and leak diagnosis build on it. In this lesson we only draw; the next lesson uses the drawing to explain sharing.
Learning Objectives
By the end of this lesson you will be able to:
- Explain what a stack frame is and what it holds.
- Say where primitive values and object values live.
- Explain what a reference is and why a variable of object type is not the object itself.
- Draw the stack and heap for a short program.
- Say what happens to a frame when its method returns, and what does not happen to heap objects.
- Recognise that arrays are objects and live on the heap.
Written Explanation
The stack: one frame per running method call
When a method is called, the JVM (the Java Virtual Machine, the program that runs your compiled code) creates a frame for that call and places it on top of the stack. The frame holds the method's parameters and local variables. When the method returns, its frame is removed, and the variables inside it are gone. You saw this behaviour in BEG-014: each call has its own variables, and the caller resumes when the callee finishes.
Because frames are stacked, the one on top is the method currently running. main sits at the bottom, since it was called first.
The heap: where objects live
Every object created with new is placed on the heap. The heap is shared: any frame that has the object's address can use it, and the object does not disappear when the method that created it returns. Its lifetime is decided by something else (lesson 3).
Primitives versus references
A local variable of a primitive type (int, double, boolean, and the others from BEG-005) stores its value directly in the frame. A local variable of any class type (String, Person, List<String>, an array) stores a reference: a value that identifies an object on the heap. In diagrams we draw it as an arrow.
STACK (desk) HEAP (warehouse)
main frame
age [ 30 ]
p [ ●---------------------> Person { name ─────> "Ana" }Notes on the diagram:
ageholds30itself.pholds a reference. ThePersonobject, with its own fieldname, lives on the heap.- Fields of an object live inside the object, on the heap. A field of class type holds a reference too, so the diagram has an arrow from the object to the
String. nullis the reference that points to nothing. We draw it as an arrow to nowhere, or writenullin the box.
A drawing recipe
For any short program, draw in this order:
- One box per frame,
mainat the bottom, newer calls above. - Inside each frame, one slot per parameter and local variable, in the order they are declared.
- For each
new, a box on the heap with its fields. - For each variable of class type, an arrow from its slot to the object (or
null). - When a method returns, cross out its frame. Leave the heap boxes alone.
You do not need to track exact addresses; arrows are enough. (Real JVMs decide how to store objects and may lay them out differently. The picture is a mental model, not a memory dump.)
What this means for lifetimes
- A local variable's life matches its frame: it ends when the method returns.
- An object's life does not depend on the method that created it. If a method creates an object and returns the reference, the object is still there; the caller's variable now points to it.
That is why Person make() can return a new Person safely. What dies is the method's frame and its sticky notes, not the warehouse item.
Examples
Example 1: a primitive and an object (simple)
public class L01a {
static class Person {
String name;
Person(String name) { this.name = name; }
}
public static void main(String[] args) {
int age = 30;
Person p = new Person("Ana");
System.out.println(age + " " + p.name);
}
}Output:
30 AnaAt the println, the stack holds one frame:
STACK HEAP
main
args [ ●-----> String[] (empty array, supplied by the JVM)
age [ 30 ]
p [ ●-----> Person { name ●-----> "Ana" }args is a parameter, so it has a slot, and the array is an object on the heap.
Example 2: a method call and a returned object (practical)
static Person make(String n) {
int len = n.length();
Person tmp = new Person(n);
System.out.println("in make, len = " + len);
return tmp;
}
public static void main(String[] args) {
Person p = make("Omar");
System.out.println("back in main: " + p.name);
}(Person is the same nested class as above.) Output:
in make, len = 4
back in main: OmarWhile make runs, there are two frames:
STACK HEAP
make (top)
n [ ●-----> "Omar" ] <---------- same String object
len [ 4 ]
tmp [ ●-----> Person { name ●-----> "Omar" }
main
args [ ●-----> String[] ]
p [ (not assigned yet) ]When make returns, its frame is removed: n, len, and tmp are gone. The Person is untouched, and main's p receives the reference. Notice n and the name field point to the same String object; the heap objects can be shared by several arrows. Lesson 2 builds on this.
Example 3: an array of objects (realistic)
Account[] accounts = new Account[2];
accounts[0] = new Account("Lina", 100.0);
System.out.println(accounts[0].owner + " " + accounts[0].balance);
System.out.println(accounts[1]);(Account is a small class with owner and balance.) Output:
Lina 100.0
nullAn array is an object, so it lives on the heap. The array of class type holds references, one per slot, and a new slot is null until you assign it:
STACK HEAP
main
accounts [ ●-----> Account[2] { [0] ●-----> Account { owner "Lina", balance 100.0 }
[1] null }Creating the array did not create any Account; only the one new Account(...) did. This explains why accounts[1].owner would fail with a NullPointerException: there is no object at the end of that arrow (BEG-037 covered how to avoid that).
Backend Insight
A backend service handles many requests at the same time. Each request typically runs on its own thread, and each thread has its own stack, with its own frames and local variables. The heap, by contrast, is shared by all threads. (Threads are taught later; for now, remember that "stack = per call chain, heap = shared".)
This explains common real-world facts:
- Objects such as a loaded configuration or a cache live on the heap and are visible to every request.
- Local variables are private to one request's call chain, which is why they are naturally safe from interference.
- When people talk about a server's "memory usage", they mostly mean the heap. Stacks are small and bounded.
When you later read a stack trace in a log (BEG-029), you are reading a printed copy of the stack frames at the moment of an exception, top frame first.
Common Mistakes
Mistake 1: "The object is inside the variable"
What goes wrong: you picture Person p as a box containing the person. Then assignments and method calls (next lesson) seem to behave randomly.
Fix: always draw an arrow from the variable's slot to a separate heap box.
Mistake 2: "The object disappears when the method ends"
What goes wrong: you avoid returning objects, or you think a returned Person is dangling.
Fix: the frame disappears, not the object. The object lives as long as something can still reach it (lesson 3).
Mistake 3: "Everything lives on the heap" or "everything on the stack"
What goes wrong: you cannot predict what is shared.
Fix: local variables and parameters are in frames. Objects, including arrays and the contents of fields, are on the heap. Treat a primitive as a value in the slot and an object as an arrow.
Mistake 4: forgetting the extra objects
What goes wrong: new Account[2] is drawn as two accounts.
Fix: it makes one array object with two null slots. Each new makes exactly one object (plus whatever its constructor creates).
Exercises
Level A: Foundation
- Fill in the blank: local variables of a running method live in a ______, and objects created with
newlive on the ______. - For
int n = 7; String s = "go";, state which variable holds the value itself and which holds a reference. - True or false: when
makereturns thePersonit created, thatPersonis removed along withmake's frame.
Level B: Application
- Draw stack and heap at the final line of:
public static void main(String[] args) {
double price = 9.5;
String label = "tea";
int[] counts = new int[3];
counts[1] = 4;
}- A method
static Account open(String owner)createsnew Account(owner, 0)and returns it;maincallsAccount a = open("Sami");. List which frames exist whileopenruns, and what remains after it returns. - In
Person p = null;, does aPersonobject exist on the heap? What does the slot ofpcontain?
Level C: Challenge
- Draw the picture at the
printlnin:
static int total(int[] values) {
int sum = 0;
for (int v : values) sum += v;
System.out.println(sum);
return sum;
}
public static void main(String[] args) {
int[] data = {2, 3, 5};
int result = total(data);
}Which frame holds values, and where does the array live?
أظهر الحلول
- Stack frame; heap.
nholds the value7itself.sholds a reference to aStringobject on the heap.- False. The frame is removed, but the
Personlives on the heap and the caller receives its reference. - Frame
main:argswith an arrow to an emptyString[];price=9.5;labelwith an arrow to aString "tea";countswith an arrow to anint[3]object on the heap whose contents are0, 4, 0. (A string literal is aStringobject; the exact place where the JVM stores literals is an implementation detail, so drawing it on the heap is fine at this level.) - While
openruns:main(bottom) andopen(top). After the return: onlymain.open's frame, withownerand any other locals, is gone; theAccounton the heap remains, andainmainpoints to it. - No. The slot of
pholdsnull, which means "refers to no object". The heap contains noPerson. - Frames:
total(top) withvaluespointing to the array,sum=10,v(only inside the loop);mainwithargs,datapointing to the array, andresultnot yet assigned. There is one array on the heap,{2, 3, 5}, and bothvaluesanddatapoint to it. The array is shared: it was not copied. (Lesson 2 explains this fully.)
Mini Challenge
Take any class from an earlier module (for example Account from Bank Accounts) and write a three-line main that creates two objects and keeps one of them in a local variable. On paper, draw the stack and heap. Then add a one-sentence caption saying which part would disappear if main were a method that had just returned. Keep the drawing: you will reuse it in lesson 2.
Summary & Next Lesson
Key takeaways
- The stack has one frame per running method call; frames hold parameters and locals and disappear on return.
- Primitives are stored directly in the slot; class-type variables store references to heap objects.
newcreates one object on the heap. Arrays are objects too.- A frame is short-lived; an object lives as long as it can still be reached.
- Draw arrows, not boxes inside boxes.
Next lesson
Next is References and aliasing: what happens when two variables point to the same object, and what that means when you pass objects to methods.
Self-check
- What does a stack frame hold?
- Where does the
Personcreated bynew Person("Ana")live? - What does
String s;hold inside a method afters = null;? - After
int[] a = new int[4];, how many objects are on the heap, and what do its elements hold? - When a method returns, what is removed from the stack?
أظهر الإجابات
- The method's parameters and local variables (and bookkeeping the JVM needs to resume the caller).
- On the heap. Only the reference is in the frame.
null, meaning no object.- One (the array); its four elements are
intvalues, each0. - That method's frame and all its local variables.
References
- The Java Tutorials: Passing Information to a Method: verified that object arguments are references passed by value, which supports the picture of a reference copied into a slot. The primitive-versus-reference split comes from earlier course modules; I did not separately verify it against the JLS.
- Frame and heap layout are JVM implementation details; the diagrams here are a teaching model. I compiled and ran the three examples with JDK 21 and copied their output.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.