Garbage Collection in Java
Learn how the JVM automatically identifies unreachable objects and reclaims heap memory.
What is Garbage Collection?
Garbage Collection, commonly called GC, is the JVM's automatic memory-management mechanism for reclaiming heap memory occupied by objects that are no longer reachable.
Developers normally do not explicitly free Java objects. Instead, the garbage collector determines which objects are no longer reachable and may reclaim their memory.
Why Does Java Need Garbage Collection?
In languages with manual memory management, developers often need to explicitly release dynamically allocated memory. Java instead provides automatic memory management through the JVM.
Object Reachability
Garbage collection is based primarily on reachability. An object is generally considered eligible for collection when it can no longer be reached through references from GC roots.
Student student = new Student();
student.name = "Samadhan";
student = null;
After student is assigned null,
the previously created object may become unreachable if
there are no other references to it.
GC Roots
The garbage collector determines reachability starting from special references known as GC roots.
| GC Root Category | Example |
|---|---|
| Local variables | References held by active stack frames. |
| Active threads | Objects referenced by currently running threads. |
| Static references | Objects reachable through active class-level references. |
| JNI references | References maintained through native code. |
Eligible for Garbage Collection
An object becomes eligible for garbage collection when there are no active references pointing to it. The JVM can then reclaim the memory occupied by that object when garbage collection occurs.
Garbage Collection Example
class Demo {
@Override
protected void finalize() throws Throwable {
System.out.println("Garbage Collector executed successfully at CIIT Institute 🤓❤️...!");
System.out.println("Object is successfully 🫥 removed from Heap Memory...!");
}
}
public class Main {
public static void main(String[] args) {
Demo object = new Demo();
object = null;
System.gc();
System.out.println("Main method execution finished.");
}
}
Output
Main method execution finished.
Possible Garbage Collection messages:
Garbage Collector executed successfully at CIIT Institute 🤓❤️...!
Object is successfully 🫥 removed from Heap Memory...!
Line-by-Line Explanation
| Code | Explanation |
|---|---|
class Demo
|
Defines a class named Demo.
An object of this class is created inside
the main() method.
|
@Override
|
Indicates that the finalize()
method overrides the method inherited
from Java's Object class.
|
protected void finalize() throws Throwable
|
Defines the finalize() method.
Older Java programs used this method for
cleanup associated with garbage collection.
|
System.out.println("Garbage Collector executed successfully at CIIT Institute!");
|
Prints a message when the
finalize() method executes.
|
System.out.println("Object is successfully removed from Heap Memory.");
|
Prints a message describing the processing of the object for memory reclamation. |
public class Main
|
Defines the Main class that
contains the entry point of the Java program.
|
public static void main(String[] args)
|
This is the main method. Java starts program execution from this method. |
Demo object = new Demo();
|
Creates a new Demo object.
Memory for the object is allocated in the
heap, and object stores its
reference.
|
object = null;
|
Removes the reference from the
object variable. If there are
no other references to the object, it
becomes eligible for garbage collection.
|
System.gc();
|
Requests the JVM to consider performing garbage collection. It does not guarantee that garbage collection will happen immediately. |
System.out.println("Main method execution finished.");
|
Prints a message indicating that the main method has reached this point of execution. |
How This Program Works
Step 1:
Demo object = new Demo();
↓
Object is created in Heap Memory
Step 2:
object = null;
↓
Reference to the object is removed
Step 3:
Object has no active reference
↓
Object becomes eligible for Garbage Collection
Step 4:
System.gc();
↓
JVM receives a request to perform Garbage Collection
Step 5:
Garbage Collector may process the object
↓
Memory may be reclaimed
System.gc() only requests garbage collection.
The JVM decides whether and when garbage collection
actually occurs. The finalize() mechanism is
deprecated in modern Java and should not be used for
normal resource cleanup.
Eligible Does Not Mean Immediately Collected
Making an object unreachable does not mean that the garbage collector immediately removes it.
The JVM decides when and how garbage collection occurs based on the selected garbage collector, runtime conditions, memory pressure, and other implementation details.
Basic Garbage Collection Process
Objects allocated
↓
Objects become reachable
↓
References may disappear
↓
Unreachable objects identified
↓
Garbage collector reclaims memory
Mark-and-Sweep Concept
A simplified model of garbage collection is the mark-and-sweep approach.
- Start from GC roots.
- Mark objects that are reachable.
- Identify objects that are not marked.
- Reclaim memory associated with unreachable objects.
Generational Garbage Collection
Many JVM garbage-collection designs take advantage of the observation that many objects become unreachable shortly after they are created, while some objects survive for a longer time.
Heap
Young Generation
↓
Short-lived objects
Objects that survive
↓
Older Generation
↓
Longer-lived objects
Young Generation
Newly created objects commonly begin their lifetime in the young generation in JVM garbage collectors that use a generational design.
Objects that survive collection cycles can be promoted to older regions.
Old Generation
Objects that remain reachable for longer periods may be promoted into older regions of the heap.
Collection behavior differs between garbage collectors, so applications should not rely on a particular internal heap layout.
Minor, Major, and Full GC Terminology
Terms such as minor GC, major GC, and full GC are commonly used when discussing JVM garbage collection. Their exact meaning can depend on the garbage collector and JVM version.
| Term | General Meaning |
|---|---|
| Minor GC | Often refers to collection activity focused on young-generation regions. |
| Major GC | A commonly used term for collection involving older regions, although terminology varies. |
| Full GC | Generally refers to collection involving broader parts of the heap and potentially other memory-management work. |
Garbage Collectors
The JVM provides different garbage collectors optimized for different workload characteristics.
| Collector | General Focus |
|---|---|
| Serial GC | Simple collector that uses a single thread for GC work. |
| Parallel GC | Designed to use multiple threads for collection and emphasizes throughput. |
| G1 GC | Region-based collector designed to balance throughput and pause-time goals. |
| ZGC | Low-latency collector designed for very short pause times on large heaps. |
| Shenandoah | Collector designed to reduce pause times by performing more work concurrently. |
Stop-the-World Pauses
Some garbage-collection operations require application threads to pause temporarily. Such pauses are commonly described as stop-the-world pauses.
Modern low-latency collectors perform much of their work concurrently to reduce the duration of application pauses.
System.gc()
System.gc();
This method requests that the JVM consider performing garbage collection. It does not guarantee that garbage collection will happen immediately.
System.gc() as a normal strategy for
managing application memory.
Finalization
Older Java code sometimes used the
finalize() mechanism for cleanup.
Finalization has been deprecated and should not be used as a modern resource-management technique.
For resources such as files, sockets, and database
connections, use explicit resource-management mechanisms
such as try-with-resources.
Resource Management with try-with-resources
try (BufferedReader reader =
new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
System.out.println(line);
}
Garbage collection manages memory, but resources such as files should be closed explicitly through appropriate resource-management APIs.
Garbage Collection Does Not Prevent Memory Leaks
Java applications can still experience memory leaks when unnecessary objects remain reachable.
class Cache {
static List<Object> values = new ArrayList<>();
static void add(Object value) {
values.add(value);
}
}
Objects stored in the static collection remain reachable through the collection. If the collection grows without limits, memory usage can grow continuously.
Weak References
Java provides reference types such as
WeakReference for use cases where an object
should not be kept strongly reachable solely by that
reference.
WeakReference<Student> reference =
new WeakReference<>(new Student());
Student student = reference.get();
Weak references are useful in specialized memory-sensitive designs such as certain caches.
Monitoring Garbage Collection
When diagnosing memory or GC problems, developers can use JVM monitoring and profiling tools.
- Java Flight Recorder.
- Java Mission Control.
- JVM garbage-collection logs.
- Heap dumps and memory analyzers.
- Application Performance Monitoring tools.
Garbage Collection Best Practices
- Avoid unnecessary object creation.
- Avoid retaining objects longer than required.
- Avoid unbounded static collections and caches.
- Use try-with-resources for closeable resources.
-
Do not depend on
System.gc()for normal memory management. - Profile the application before changing JVM memory settings.
Interview Questions
Summary
Java uses automatic garbage collection to reclaim heap memory from unreachable objects. Understanding reachability, GC roots, heap behavior, collector choices, memory leaks, and explicit resource management is important for building reliable Java applications.