In our last lesson, you learned how to define a class—the blueprint for an object—and how to bring that blueprint to life by creating an object instance with the new keyword. Today, we'll delve into what happens "under the hood" when you write a line like Student student1 = new Student("Aisha", 101);. We will explore the two fundamental memory areas the Java Virtual Machine (JVM) uses: the stack and the heap.
Understanding this memory model is a cornerstone of effective Java programming. It helps in writing efficient code, diagnosing performance issues, and preventing common errors. It is also a frequent topic in technical interviews, as it demonstrates a deeper understanding of the language's runtime behavior. By the end of this lesson, you will be able to explain exactly where different parts of your program reside in memory and why that distinction matters.
1. The Two Primary Memory Areas: Stack and Heap
When your Java program runs, the JVM organizes the memory available to it into several areas. For our purposes, the two most important are the Stack Memory and the Heap Space. They serve different purposes and have distinct characteristics.
- Stack Memory: Think of this as a highly organized, temporary workspace. It's used to manage the execution of methods. Each thread in your application gets its own stack.
- Heap Space: This is a large, shared memory pool for the entire application. It's where all objects live, regardless of which method created them.
The following article from Baeldung provides an excellent breakdown of these two memory areas. We'll start by reading the definitions and key features of each.
Stack Memory and Heap Space in Java
This article offers a clear and professional overview of Java's core memory model.
Please read the sections "Stack Memory in Java" and "Heap Space in Java". As you read, focus on the following key differences: What type of data is stored in each? How is memory allocated and deallocated? What is the lifetime of the data in each area? Which one offers faster access?
To summarize the key points from your reading:
| Feature | Stack Memory | Heap Space |
|---|---|---|
| Purpose | Method execution, local variables, references | Storing objects and their instance variables |
| Lifecycle | Short-lived (duration of the method call) | Long-lived (as long as the object is referenced) |
| Allocation | Automatic, LIFO (Last-In, First-Out) | Dynamic, managed by the Garbage Collector |
| Access Speed | Very Fast | Slower than Stack |
| Size | Fixed per thread, relatively small | Large, shared by all threads |
| Error Type | StackOverflowError |
OutOfMemoryError |
2. Tracing Memory Allocation: A Step-by-Step Example
Now, let's visualize how the stack and heap work together when a program runs. Consider this simple code snippet that uses the Student class from our previous lesson.
public class University {
public static void main(String[] args) {
// 1. main method starts
int id = 101;
// 2. A primitive local variable is created
Student student1 = buildStudent("Ben", id);
// 3. A method is called, which creates an object
student1.study();
// 4. A method is called on the object
} // 5. main method ends
public static Student buildStudent(String name, int studentId) {
Student s = new Student(name, studentId);
return s;
}
}
Here is what happens in memory at each step:
-
mainmethod starts: The JVM invokesmain. A new "frame" is pushed onto the Call Stack to managemain's execution. A corresponding block of memory is reserved in Stack Memory formain's local variables. -
int id = 101;: The variableidis a primitive type. Its value,101, is stored directly insidemain's block on the Stack. -
Student student1 = buildStudent(...):- The
buildStudentmethod is called. A new frame is pushed onto the Call Stack (on top ofmain). A new memory block for its local variables (name,studentId,s) is created on the Stack. - Inside
buildStudent, thenew Student(...)expression is executed. Thenewkeyword tells the JVM to allocate memory for aStudentobject in the Heap. This object holds its own data for its fields (name and studentId). - The memory address of this new object in the Heap is returned by the
newoperator. - This address is stored in the local reference variable
son the Stack. We sayspoints to or references theStudentobject in the Heap. buildStudentreturns the value ofs(the memory address). AsbuildStudentfinishes, its frame is popped from the Call Stack, and its memory block on the Stack is cleared.- Back in
main, the returned address is stored in the local reference variablestudent1. Now,student1(on the Stack) points to the sameStudentobject in the Heap.
- The
-
student1.study();: The JVM uses the address stored instudent1to locate theStudentobject in the Heap and then invokes itsstudy()method. -
mainmethod ends: Themainmethod finishes execution. Its frame is popped from the Call Stack, and its memory on the Stack (including theidvalue and thestudent1reference) is cleared.
At this point, there are no more references pointing to the Student object in the Heap. It has become "unreachable." Eventually, Java's Garbage Collector (GC) will find this orphaned object and reclaim its memory, making it available for new objects.
The diagram below, taken from the Baeldung article, illustrates a very similar process. It shows how method calls get added to the Call Stack, how local variables and references live in Stack Memory, and how the actual objects reside in Heap Space.

3. Reinforcing with a Visual Explanation
Reading about this process is one thing, but seeing it animated can make it click. The following video provides a dynamic walkthrough of these concepts.
Heap Space vs. Stack Memory in Java | Call Stack Explained | Memory Allocation | Geekific
This video from Geekific provides an excellent animated walkthrough of the entire memory allocation process, clearly distinguishing the Call Stack from Stack Memory.
Watch the segment from the code example. Follow along as the program executes and see how: The main method is pushed to the Call Stack. Primitive variables (id) are stored directly on the Stack Memory. Object references (channel) are created on the Stack, pointing to objects in the Heap. The String Pool (a special area in the Heap) stores string literals. New method calls (buildChannel) are pushed onto the Call Stack, and their stack frames are created. As methods complete, they are popped off the stack, and their memory is cleared.
4. Primitives vs. References: A Crucial Distinction
The most important takeaway from this lesson is the difference in how Java treats primitive types and object types in memory.
- Primitive Variables (e.g.,
int x = 10;): The variablexdirectly contains the value10. It lives entirely on the stack (if it's a local variable). If you passxto a method, a copy of the value10is made. - Object References (e.g.,
Student s = new Student();): The variablesdoes not contain theStudentobject. It contains the memory address of the object, which lives on the heap. If you passsto a method, a copy of the address is made. Both the original reference and the copy point to the exact same object in the heap.
This model of variables as named references pointing to objects in a shared memory space might feel familiar from your experience with Python, where all variables, including numbers and strings, behave this way. In Java, this distinction between primitives (pass-by-value) and objects (pass-by-reference-value) is explicit and fundamental.
Conclusion
You've now peered behind the curtain to see how the JVM manages memory during program execution. This is a critical piece of the puzzle for understanding how Java works at a deeper level.
Here are the key takeaways:
- Stack Memory is for method execution frames. It stores local variables and references to objects. It is fast, automatically managed (LIFO), and short-lived.
- Heap Space is for the objects themselves, created with the
newkeyword. It is a large, shared pool of memory managed by the Garbage Collector. - Primitives (like
int,double,boolean) have their values stored directly on the stack. - Objects have their data stored on the heap, while the variables that refer to them are references (memory addresses) stored on the stack.
Now that you understand how an object's data is stored, the next logical step is to consider how to protect and manage access to that data. In our next lesson, we will explore encapsulation, a core principle of OOP. You'll learn how to use access modifiers (public, private) to hide an object's internal state and expose controlled behaviors, leading to more robust and maintainable code.