Skip to main content

Welcome to a Grasp lesson

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

Lesson 17

Generational Garbage Collection Explained

Recommended resources

In our last lesson, we established the foundational principles of garbage collection, focusing on how the JVM identifies unreachable objects using GC roots and the mark-and-sweep algorithm. We concluded by noting that repeatedly scanning the entire heap is inefficient, especially for large applications. This sets the stage for a powerful optimization that virtually all modern JVMs employ.

This lesson delves into that optimization: generational garbage collection. Our objective is to understand the core principle that makes this approach so effective—the weak generational hypothesis—and to describe how it translates into a specialized heap structure and collection process. This concept is central to achieving high performance and low latency in Java applications.

The Weak Generational Hypothesis

The simple mark-and-sweep algorithm treats all objects equally. It doesn't matter if an object was created a microsecond ago or at the start of the application; the collector must traverse it. However, empirical studies of real-world applications have revealed a consistent pattern in object lifetimes. This observation is the key to a much more efficient GC strategy.

3 Generations

This document from the official Oracle Java documentation explains the motivation behind generational collection.

Read the introduction and the section describing the weak generational hypothesis. Pay close attention to the graph in "Figure 3-1" and its description, which visualizes the typical lifetime distribution of objects.

As you've just read, the weak generational hypothesis makes two primary assertions:

  1. Most objects die young. A large percentage of objects become unreachable very shortly after they are created. Think of temporary variables like iterators inside a loop or data transfer objects used for a single request.
  2. Few references exist from old objects to young objects. This is a corollary of the first point. Objects that survive for a long time tend to be part of a stable data structure and don't frequently point to newly created, short-lived objects.

This hypothesis suggests that a "one-size-fits-all" approach is suboptimal. Instead of repeatedly scanning the entire heap, the garbage collector could achieve better performance by focusing its efforts where the most "garbage" is likely to be found: among the youngest objects.


The Generational Heap Structure

To exploit this hypothesis, the JVM's heap is logically partitioned into different areas, or generations, based on object age. This allows the GC to apply different collection strategies to different generations.

At a high level, the heap is split into two main areas:

  • The Young Generation: This is where all new objects are allocated. Because most objects die young, this area is collected frequently and aggressively.
  • The Old Generation (or Tenured Generation): Objects that survive long enough in the Young Generation are eventually "promoted" to the Old Generation. This area is collected much less frequently.

The Young Generation itself is further subdivided to facilitate an efficient collection process.

This diagram illustrates the standard layout of the JVM heap. It is divided into the Young Generation (containing Eden and two Survivor spaces), the Old Generation (Tenured), and a Non-Heap area for metadata (Metaspace).

As the diagram shows, the Young Generation consists of:

  • Eden Space: The "garden of Eden" for objects. With very few exceptions, all new object allocations happen here.
  • Two Survivor Spaces (S0 and S1): These spaces hold objects that have survived at least one garbage collection cycle in the Young Generation. At any given time, one survivor space is in use (the "to-space") and the other is empty (the "from-space").

Now, let's see how objects move through these spaces.


The Lifecycle of an Object

The generational structure enables a highly optimized workflow for managing object memory. The entire process is a fascinating dance of allocation, collection, copying, and promotion.

The following video provides an excellent, step-by-step visualization of this process. It walks through how objects are created in Eden, how they are handled during a collection, and how they eventually get promoted.

9. Java Memory Management and Garbage Collection in Depth

This video from the "Concept && Coding" channel provides a detailed visual walkthrough of memory management in a generational heap.

Watch the segment from this section, which demonstrates the process. Focus on: Where new objects are created (allocation in Eden). What happens during a Minor GC: marking, sweeping, and copying survivors to a survivor space (the first GC). How objects "age" and move between survivor spaces (subsequent GCs). The concept of promotion to the Old Generation when an age threshold is met (aging and promotion).

Let's formalize the concepts you just saw in the video.

Allocation and Minor GC

  1. Allocation: Your application creates objects (new MyObject()), and they are placed in the Eden space.
  2. Trigger: When Eden fills up, it triggers a garbage collection event that is confined only to the Young Generation. This is called a Minor GC.
  3. Collection: During a Minor GC, the collector identifies all the live objects in Eden and the currently active Survivor space. It does this by starting from GC roots and traversing the object graph.
  4. Copying: All live objects are then copied to the other, empty Survivor space. After the copy, Eden and the first Survivor space are completely empty and can be reused for new allocations. This "mark-and-copy" approach is very efficient because, per the generational hypothesis, the vast majority of objects in Eden are dead, so there is very little to copy. It also naturally compacts the memory for live objects.

Aging and Promotion to the Old Generation

Each object has an "age," which is simply a counter for how many Minor GCs it has survived. This age is typically stored in the object's header.

  • When an object survives a Minor GC and is copied from Eden to a Survivor space, its age is set to 1.
  • On each subsequent Minor GC that it survives, its age is incremented as it's copied from one Survivor space to the other.

Eventually, if an object continues to survive, its age will reach a predefined limit.

Understanding JVM Garbage Collection – Part 3 - A Bias For Action

This article provides a more detailed breakdown of the generational hypothesis and the mechanics of collection within each generation.

First, read the sections on the hypothesis itself and the basic algorithms for each generation. Then, dive into the detailed descriptions of the Young Generation and the Old Generation. Pay special attention to the mention of the MaxTenuringThreshold flag, which controls the age at which objects are promoted.

When an object's age reaches the tenuring threshold (controlled by the -XX:MaxTenuringThreshold JVM flag, which defaults to 15 in many modern JVMs), it is considered long-lived. During the next Minor GC, instead of being copied to the other Survivor space, it is promoted—moved to the Old Generation.

Major GC and Full GC

The Old Generation is collected much less frequently. When the Old Generation starts to fill up, a Major GC is triggered to clean up space there. A Major GC is a much more significant event than a Minor GC because it typically involves scanning a much larger portion of the heap with many more live objects.

Often, a Major GC is triggered as part of a Full GC, which cleans the entire heap—both the Young and Old Generations. These events are the primary cause of noticeable application pauses, which we will discuss in the next lesson.


A Complication: Inter-Generational References

The core benefit of generational GC is that a Minor GC only needs to scan the Young Generation. But what if an object in the Old Generation holds a reference to an object in the Young Generation? If the GC only scanned the Young Generation and its direct roots (like the stack), it might mistakenly garbage collect the young object.

To avoid scanning the entire Old Generation during a Minor GC, the JVM uses a mechanism called a card table.

Understanding JVM Garbage Collection – Part 3 - A Bias For Action

We'll return to the same article to understand how the JVM handles this specific problem.

Read the last part of the article, focusing on the paragraph that begins with the key challenge. This explains the concept of write barriers and card tables.

In essence, the Old Generation is divided into small regions called "cards." Whenever your application code modifies a reference in an Old Generation object (e.g., oldObject.field = youngObject;), a write barrier—a small piece of code injected by the JIT compiler—marks the corresponding card as "dirty."

During a Minor GC, the collector only needs to scan the objects within these few dirty cards in the Old Generation to find any additional roots pointing into the Young Generation. This is vastly more efficient than scanning the entire Old Generation every time.


Conclusion

In this lesson, we've dissected the logic behind generational garbage collection, the dominant strategy used in the JVM. By understanding the lifecycle of objects, we can see how the JVM optimizes memory management for the most common case.

Key Takeaways:

  • Weak Generational Hypothesis: The foundational principle stating that most objects have short lifecycles.
  • Generational Heap: The heap is divided into a Young Generation (for new, short-lived objects) and an Old Generation (for long-lived objects).
  • Young Generation Structure: Comprises an Eden space for initial allocation and two Survivor spaces for copying survivors.
  • Minor GC: A frequent and fast collection event that cleans only the Young Generation, typically using a mark-and-copy algorithm.
  • Aging and Promotion: Objects that survive multiple Minor GCs have their "age" incremented. Once they reach a tenuring threshold, they are promoted to the Old Generation.
  • Major GC: An infrequent and more costly collection of the Old Generation.
  • Card Tables: An efficient mechanism to track references from the Old Generation to the Young Generation, avoiding the need to scan the entire Old Generation during a Minor GC.

We've established how generational GC works. In our next lesson, we will confront a critical consequence of all GC activity: stop-the-world pauses. We will explore what they are, why they are necessary, and how they impact the perceived performance and responsiveness of your applications.