Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 9

Stack vs. Heap Memory for Object Creation

Recommended resources

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:

  1. main method starts: The JVM invokes main. A new "frame" is pushed onto the Call Stack to manage main's execution. A corresponding block of memory is reserved in Stack Memory for main's local variables.

  2. int id = 101;: The variable id is a primitive type. Its value, 101, is stored directly inside main's block on the Stack.

  3. Student student1 = buildStudent(...):

    • The buildStudent method is called. A new frame is pushed onto the Call Stack (on top of main). A new memory block for its local variables (name, studentId, s) is created on the Stack.
    • Inside buildStudent, the new Student(...) expression is executed. The new keyword tells the JVM to allocate memory for a Student object 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 new operator.
    • This address is stored in the local reference variable s on the Stack. We say s points to or references the Student object in the Heap.
    • buildStudent returns the value of s (the memory address). As buildStudent finishes, 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 variable student1. Now, student1 (on the Stack) points to the same Student object in the Heap.
  4. student1.study();: The JVM uses the address stored in student1 to locate the Student object in the Heap and then invokes its study() method.

  5. main method ends: The main method finishes execution. Its frame is popped from the Call Stack, and its memory on the Stack (including the id value and the student1 reference) 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.

This diagram shows the relationship between the Call Stack, Stack Memory, and Heap Space. Method calls (`main`, `buildPerson`, `Person` constructor) create frames on the Call Stack. Each frame's local variables and references are stored in Stack Memory. The actual `Person` object and the `"John"` String literal are stored in Heap Space, with references from the stack pointing to them.

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 variable x directly contains the value 10. It lives entirely on the stack (if it's a local variable). If you pass x to a method, a copy of the value 10 is made.
  • Object References (e.g., Student s = new Student();): The variable s does not contain the Student object. It contains the memory address of the object, which lives on the heap. If you pass s to 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 new keyword. 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.