Memory Management in Java

โšก Smart Summary

Memory Management in Java explains how the JVM divides runtime memory across Stack, Heap, Code, and Static regions, how object references flow during method calls, and how the Garbage Collector reclaims unreachable objects so applications stay stable and free of memory leaks.

  • ๐Ÿง  JVM Memory Layout: Java memory is split into Heap for objects, Stack for method frames and local variables, plus dedicated areas for bytecode and static data.
  • ๐Ÿ“š Stack vs Heap: Stack frames follow Last-In-First-Out order and store primitives and references, while the Heap holds every object created with the new operator.
  • โ™ป๏ธ Garbage Collection: The Garbage Collector tracks reachability through reference chains and automatically reclaims memory from objects that no thread can reach.
  • โœ… Modern Collectors: Production workloads increasingly rely on G1, ZGC, and Shenandoah for low-pause collection across multi-gigabyte heaps.
  • ๐Ÿงช Hands-On Example: A step-by-step Student class walkthrough shows when references become eligible for collection and how nulling references frees the underlying object.

Memory Management in Java

What is Stack Memory in Java?

Stack memory in Java is the region of JVM memory that stores method frames, local variables, and reference variables for each thread. The Stack is always accessed in Last-In-First-Out order, so the most recently invoked method sits on top, and its local variables are pushed and popped with the frame.

Each thread in the Java Virtual Machine receives its own Stack, which keeps method invocations isolated and thread-safe by design. Primitive locals such as int, boolean, and double live directly inside the frame, while object references stored on the Stack point to objects allocated on the Heap.

What is Heap Memory in Java?

Heap memory is the shared JVM region that holds every Java object and array created with the new operator, along with any reference variables that belong to those objects as instance fields. Unlike the Stack, the Heap is shared across all threads, which is why object access often requires synchronization.

The Heap is the area the Garbage Collector manages. Modern HotSpot JVMs divide the Heap into a Young Generation for short-lived objects and an Old Generation for long-lived objects, with class metadata stored in a separate native region called Metaspace.

Memory Allocation in Java

Memory Allocation in Java is the process by which the JVM sets aside virtual memory regions for variables and instances of classes and structures during program execution. The memory is not allocated to an object at declaration; only a reference is created. The actual object allocation happens through the new operator, so every object lives on the Heap.

The Java memory allocation is divided into the following sections:

  1. Heap
  2. Stack
  3. Code
  4. Static

This division of memory is required for effective management of the runtime.

  • The Code section contains your compiled bytecode.
  • The Stack section stores methods, local variables, and reference variables.
  • The Heap section contains objects and may also contain reference variables held as instance fields.
  • The Static section holds static data and static methods shared across all instances.

Difference between Local and Instance Variable

Understanding where each kind of variable lives in the JVM helps explain Stack and Heap behavior. An instance variable is declared inside a class but outside any method, and it lives on the Heap as part of its object.

class Student{ 
int num; // num is instance variable 
public void showData{}

A local variable is declared inside a method, including method arguments, and lives on the Stack inside the active frame.

public void sum(int a){

int x = a + 3;

// a, x are local variables

}

Difference between Stack and Heap

The Stack and the Heap solve different problems inside the JVM. The Stack gives each thread fast, deterministic allocation for short-lived data tied to method scope, while the Heap provides a shared region for long-lived objects that any thread can reference. The short video below summarizes the contrast before the walkthrough that follows.

Click here if the video is not accessible

To see how Stack and Heap cooperate, consider a main method that calls method m1.

public void m1{
int x = 20;
}

In the JVM Stack, a frame is created for method m1.

Java Stack and Heap

The variable x in m1 is also created in the frame for m1 on the Stack, as shown in the image below.

Java Stack and Heap

Method m1 then calls method m2. In the Stack, a new frame is created for m2 on top of the frame for m1.

Java Stack and Heap

Java Stack and Heap

The local variables b and c are also created inside the frame for m2 on the Stack.

public void m2(int b){
boolean c;
}

Next, m2 calls method m3. Again, a frame for m3 is created on the top of the Stack, as shown below.

Java Stack and Heap

Java Stack and Heap

Now say method m3 creates an object for class Account, which has two instance variables int p and int q.

class Account {
    int p;
    int q;
}

Here is the code for method m3.

public void m3(){
    Account ref = new Account();
    // more code
}

The statement new Account() creates an object of Account on the Heap.

Java Stack and Heap

The reference variable ref is created on the Stack inside the frame for m3.

Java Stack and Heap

The assignment operator makes the reference variable point to the object on the Heap.

Java Stack and Heap

Once the method completes execution, control returns to the calling method, which in this case is method m2.

Java Stack and Heap

The frame for method m3 is flushed out of the Stack.

Java Stack and Heap

Since the reference variable no longer points to the object on the Heap, that object becomes eligible for garbage collection.

Java Stack and Heap

Once method m2 has finished, it is popped off the Stack and all its variables become unavailable. The same happens for method m1, and eventually control returns to the main method.

What if an object holds another reference as its instance variable?

public static void main(String args[]) {
    A parent = new A();
    // more code
}
class A {
    B child = new B();
    int e;
}
class B {
    int c;
    int d;
}

In this case, the reference variable child lives on the Heap as part of the A object, and in turn points to its own B object, as shown below.

Java Stack and Heap

What is Garbage Collection in Java?

Garbage Collection in Java is the process by which the JVM performs memory management automatically. The Garbage Collector finds objects that are no longer reachable from any live reference and reclaims their memory. Dynamic memory allocation happens through the new operator, and the memory stays allocated until the program no longer holds any reference to the object.

When no references remain, the object is considered no longer needed, and the memory it occupies can be reclaimed. There is no explicit need to destroy an object because Java handles the deallocation automatically through the Garbage Collector.

The technique behind this is known as Garbage Collection. Programs that fail to release memory eventually crash when nothing remains to allocate. Such programs are said to have memory leaks. Garbage Collection in Java runs automatically across the lifetime of the program, which removes the burden of manual deallocation and reduces the risk of leaks.

In C, by contrast, the programmer is responsible for releasing memory allocated dynamically through the free() function. This is where Java memory management offers a strong advantage.

Modern HotSpot JVMs ship several Garbage Collectors tuned for different workloads. The default G1 Garbage Collector targets balanced throughput and pause times on multi-gigabyte heaps. ZGC and Shenandoah aim for sub-millisecond pauses on very large heaps, which makes them attractive for latency-sensitive services. Choosing the right collector and tuning the Young Generation, Old Generation, and Metaspace sizes is a core skill for Java performance work.

Note: All objects are created in the Heap section of memory, which is the region the Garbage Collector manages.

Example: To Learn Garbage Collector Mechanism in Java

This walkthrough shows when references become eligible for Garbage Collection inside a simple program.

Step 1) Copy the following code into an editor.

class Student{
int a;
int b;
  public void setData(int c,int d){
    a=c;
    b=d;
  }
  public void showData(){
    System.out.println("Value of a = "+a);
    System.out.println("Value of b = "+b);
  }
  public static void main(String args[]){
    Student s1 = new Student();
    Student s2 = new Student();
    s1.setData(1,2);
    s2.setData(3,4);
    s1.showData();
    s2.showData();
    //Student s3;
    //s3=s2;
    //s3.showData();
    //s2=null;
    //s3.showData();
    //s3=null;
    //s3.showData();
  }
}

Step 2) Save, compile, and run the code. As shown in the diagram, two objects and two reference variables are created.

 Garbage Collector Mechanism

Step 3) Uncomment lines 20, 21, and 22. Save, compile, and run the code.

Step 4) As shown in the diagram below, two reference variables now point to the same object.

Garbage Collector Mechanism

Step 5) Uncomment lines 23 and 24. Save, compile, and run the code.

Step 6) As shown below, s2 becomes null, but s3 still points to the object, so the object is not yet eligible for Garbage Collection.

 Garbage Collector Mechanism

Step 7) Uncomment lines 25 and 26. Save, compile, and run the code.

Step 8) At this point, no references point to the object, so it becomes eligible for Garbage Collection. The Garbage Collector removes it from memory, and there is no way to retrieve it.

 Learn Garbage Collector

How to Delete an Object in Java?

Java does not provide a manual delete operator, so the standard approach is to remove every reference to the object so the Garbage Collector can reclaim it.

1) To make an object eligible for Garbage Collection, assign every reference variable that points to it to null.

2) Primitive types are not objects, so they cannot be assigned null. Their storage is reclaimed automatically when the surrounding Stack frame is popped.

How to delete an object in Java

FAQs

The Stack stores method frames, local variables, and references in Last-In-First-Out order per thread. The Heap stores all objects created with new and is shared across threads, which is why the Garbage Collector manages it.

The Garbage Collector walks reference chains from GC roots such as active threads and static fields. Any object that cannot be reached from a root is considered unreachable and becomes eligible for reclamation during the next collection cycle.

The Young Generation holds short-lived objects in Eden and Survivor spaces. The Old Generation holds objects that survive multiple collections. Metaspace is a native-memory region that stores class metadata and replaced the older PermGen in Java 8.

G1 is the default and suits most workloads on multi-gigabyte heaps. ZGC and Shenandoah target sub-millisecond pauses on very large heaps and are good choices for latency-sensitive services running on Java 17 or later.

Set every reference that points to the object to null, or let the references fall out of scope when their Stack frames are popped. Once no live reference reaches the object, the Garbage Collector reclaims its Heap memory.

Yes. AI-driven tuning tools analyze GC logs, allocation rates, and pause histograms to recommend Heap sizes, collector flags, and Young Generation ratios. They shorten the feedback loop compared with manual tuning, especially across G1, ZGC, and Shenandoah workloads.

AI-based observability platforms ingest heap dumps and live metrics, then cluster retained object graphs to flag suspect classes and reference chains. This surfaces leak candidates faster than manual heap analysis and helps teams pinpoint the root cause in production.

Summarize this post with: