Skip to main content
Tech blog

Explain Garbage Collection (CMS vs G1 GC)?

By Markian MumbaSeptember 29, 2025
TechGarbage CollectionMemory managementHeap
peasant family at the table

Peasant Family at the Table,Vincent van Gogh

In an earlier article I had mentioned about the Java interview question... well this happens to be the second question on the list.So what is garbage collection? Being that this is centered around Java, most paradigms that I will be discussing will be about Java and the JVM and how they handle this.This is the act of removing unused objects from memory to clear space for the running program. We can simply put it as a programming language feature that automatically manages memory allocation and deallocation for objects that have been created in the Eden space (more on this later).

Understanding the Heap

When a Java program is running in memory it creates a heap. A heap is a large storage area in memory that keeps all objects the program creates. When you write new HashMap() or new String("hello"), the objects don't live in the method's local variables - they live in the shared heap space. The variables just hold references pointing to where those objects actually sit in the heap.

Code
public class HeapExample {
    public static void main(String[] args) {
        // These reference variables live on the "stack" (method memory)
        HashMap<String, String> hashMap;
        Hashtable<String, String> hashTable;
        ConcurrentHashMap<String, String> concurrentMap;
        
        // But when we create the actual objects, they go into the "heap"
        hashMap = new HashMap<>();           // HashMap object created in heap
        hashTable = new Hashtable<>();       // Hashtable object created in heap  
        concurrentMap = new ConcurrentHashMap<>(); // ConcurrentHashMap object created in heap
        
        // When we add data, those String objects also go in the heap
        hashMap.put("name", "Alice");        // "name" and "Alice" strings go in heap
        hashTable.put("age", "25");          // "age" and "25" strings go in heap
        concurrentMap.put("city", "Boston"); // "city" and "Boston" strings go in heap
        
        // Even if we pass these to other methods, the objects stay in the heap
        processCollections(hashMap, hashTable, concurrentMap);
        
        // The objects remain in heap until garbage collection removes them
    }
    
    static void processCollections(HashMap<String, String> map1, 
                                 Hashtable<String, String> map2,
                                 ConcurrentHashMap<String, String> map3) {
        // These parameters are new references, but they point to the same
        // heap objects that were created in main()
        System.out.println("All collections are in the same heap space");
    }
}

Nugget: The variables are just reference holders. So when we pass this to other methods or store them in other variables, we are not passing the object but rather a reference of the object to where it's stored on the heap.

How Garbage Collection Works

The garbage collector periodically scans the heap memory to find objects that are not being used... that is, they are not being referenced. The process of garbage collection includes certain steps which include: marking, sweeping, and compacting.

  • Marking - This is the first step. This involves marking the objects that are still being referenced by the program... remember we store the reference of the object in the variable, not the object itself. This is done by starting with a set of root objects such as global variables, local variables, and method parameters, and then tracing the objects that are reachable through those roots. Objects that cannot be reached from the roots are considered eligible for garbage collection.

  • Sweeping - After the marking phase, the garbage collector sweeps through the Java heap to identify and reclaim the memory that is used by the objects that are no longer being referenced. This involves deallocating the memory that is used by the unused objects and adding it back to the free memory pool.

  • Compacting - This step consolidates the remaining objects to reduce fragmentation and improve memory efficiency.

Heap Memory Organization

To ensure the garbage collector works efficiently, the JVM separates the heap into separate spaces.

Java Eden Space

The Eden space in Java is a memory pool where objects are created. When the Eden space is full, the garbage collector either removes objects if they are no longer in use or stores them in the survivor space if they are still being used. This space is considered part of the young generation in the memory heap.

Java Survivor Space

There are two survivor spaces in the JVM: survivor zero and survivor one. This space is also part of the young generation.

Java Tenured Space

The tenured space is where long-lived objects are stored. Objects are eventually moved to this space if they survive a certain number of garbage collection cycles. This space is much larger than the Eden space and the garbage collector checks it less often. This space is considered the old generation in the heap.

So how do these different spaces make garbage collection more efficient? Well, garbage collection occurs most frequently in the Eden space because many new objects don't need to stay in memory for very long. However, it wouldn't make sense for the garbage collector to keep checking uncollected objects over and over, particularly if an object needs to remain in the heap for a long time. That's an inefficient use of the collector. By moving objects into survivor and tenured spaces, the garbage collector knows that there is a higher likelihood that the objects there will need to remain in memory, so it checks those areas less frequently. Because the tenured space is much larger than the Eden space, it fills up less regularly and the garbage collector doesn't check it as much. The potential downside is that the tenured space is more prone to memory leaks since it isn't checked as regularly.

Garbage Collection Types

The garbage collection cycles in the young generation (Eden and survivor spaces) are considered minor garbage collection. The garbage collection cycles in the old generation (tenured space) is known as old garbage collection or major garbage collection because it takes longer than minor garbage collection. As you might guess, the minor garbage collection cycle is a simpler and faster process than major garbage collection, which makes sense because it occurs much more frequently and needs to be efficient.

What Triggers Garbage Collection?

Garbage collection is triggered by events. There are three major events that trigger garbage collection:

  • Minor event

    - This happens when the Eden space is full and objects are moved to survivor. A minor event happens within the young area.

  • Mixed events

    - These are minor events that reclaim old generation objects.

  • Major events

    - These are events that clear out the spaces in both young and old generation, which takes longer than other types of garbage collection.

Java Garbage Collection Types and Strategies

There are four different options for garbage collectors, each with their pros and their cons.

Serial Garbage Collector

This one is used for smaller single-threaded environments. Don't use it in a production environment because the process of garbage collection takes over the thread, freezing other processes. This is known as a "stop the world" event.

Parallel Garbage Collector

The parallel garbage collector is JVM's default garbage collector. As the name implies, this garbage collector uses multiple (parallel) threads. Because it can also use multiple CPUs to speed up throughput, it's also known as the throughput collector. However, when running garbage collection, it will also freeze application threads.

CMS: The Concurrent Mark Sweep Collector

Like the parallel garbage collector, the concurrent mark and sweep collector uses multiple threads. It works by running most of its garbage collection work concurrently with your application threads.

First, it performs an initial mark phase where it briefly pauses your application to identify the root objects. Then comes the concurrent marking phase, where CMS traces through object references while your application continues running. After marking, CMS does a brief remark phase to catch any changes that happened during concurrent marking. Finally, it performs concurrent sweeping, where it actually reclaims the garbage memory while your application keeps running.

This approach dramatically reduces pause times, which makes CMS excellent for applications that need consistent responsiveness. However, CMS has some significant drawbacks that become apparent under closer examination.

G1 Garbage Collector

G1 (Garbage First) represents a fundamentally different approach to the garbage collection problem. Instead of treating the heap as distinct generational regions, G1 divides the entire heap into small, equal-sized regions - typically 1MB to 32MB each.

What makes G1 revolutionary is its predictable pause times. You can tell G1 something like "never pause my application for more than 200 milliseconds," and it will do its best to honor that constraint. G1 achieves this by being selective about which regions to collect during each garbage collection cycle.

Here's the clever part: G1 continuously monitors which regions contain the most garbage relative to the time needed to collect them. During each collection cycle, it prioritizes regions with the highest garbage-to-collection-time ratio - hence the name "Garbage First."

G1 operates in cycles that alternate between young generation collections and mixed collections. During young collections, G1 collects all young regions plus any old regions that offer good garbage collection efficiency within your pause time target. The collector uses sophisticated prediction models based on historical data to estimate how long each operation will take.

For concurrent marking, G1 uses a more advanced algorithm than CMS. It employs something called "snapshot-at-the-beginning" (SATB) which handles concurrent modifications more efficiently. Think of it as taking a photograph of your restaurant's state and then tracking only the changes that matter for cleaning purposes.

When G1 performs mixed collections, it carefully selects old generation regions based on your pause time goal. If collecting a particular region would exceed your time budget, G1 leaves it for the next cycle. This predictability makes G1 excellent for applications with strict latency requirements.

💎 Random Nugget

"I suppose in the end, the whole of life becomes an act of letting go, but what always hurts the most ,is not taking a moment to say goodbye "
Irrfan Khan, The life of pi