Garbage Collection

Mrigank Singh
CodeX

--

Java provides automatic memory management unlike C/C++ through garbage collector.
“Remove objects that are not used anymore”

live object = reachable (referenced by someone) Example: cache
dead object = unreachable (not referenced from anywhere) Example: local objects in method calls.

Objects are allocated in the “heap” of Java memory. Static members, class definitions (metadata), etc., are stored in the method area (Metaspace). Metaspace is a part of non-heap memory.

Garbage collection is carried out by a daemon thread (a low-priority thread that performs background operations) called “Garbage Collector”. When new allocations cannot happen due to a full heap, you end up with a java.lang.OutOfMemoryError: heap space.

Steps in Garbage Collection

Garbage collection involves 3 steps:

  • Mark: Starts from the root node of the application (main), walks the object graph, marks objects that are reachable as live.
  • Delete/sweep: Delete unreachable objects
  • Compacting: Compact the memory by moving the objects and making the allocation contiguous than fragmented.

Generational collectors

In very crude terms, “Generation” refers to the age of objects, i.e., whether an object is young or old and how many garbage collection cycles it has survived. The heap is divided into two spaces: the New generation space and the Old generation space.

Young Generation

At a high level, the New generation serves as the initial destination for newly created objects in Java. When objects are allocated in Java code, they are specifically placed in a subsection known as the Eden space within the New generation.

As the Eden space becomes populated with objects, it eventually becomes full, triggering a Minor garbage collection event. This is where the previously mentioned marking algorithm plays a crucial role. During this phase, objects that are still referenced (marked) are moved to another subsection within the New generation known as S0 (from) in the survivor space, which is divided into two parts: S0 (from) and S1 (to). Meanwhile, objects that are unreferenced (unmarked) are cleared out by Java’s automatic garbage collection mechanism.

This pattern continues until the Eden space becomes full once more, triggering a new cycle. The events described in the previous paragraph are repeated, but with a slight difference. In this cycle, since S0 has already been populated, all the marked objects that survive from both the Eden space and S0 are moved to the second part of the survivor space, known as S1.

One very important thing to note is that any objects that make it to the survivor space are tagged with an age counter. The algorithm checks this to see if it meets a threshold to advance to the next stage: the old generation. When the Eden space becomes full once again and initiates another minor garbage collection, the marked (referenced) objects are not placed directly into S1 as before. Instead, there is a switch between the “from” and “to” survivor spaces. Objects don’t necessarily move from S0 to S1 within the survivor space. Instead, they alternate their destination with each minor garbage collection event.

Old Generation

The old generation can be perceived as the repository for long-lived objects. Essentially, if objects surpass a certain age threshold through multiple garbage collection events in the young generation, they become eligible for transfer to the old generation. When objects are garbage collected from the old generation, it triggers a major (full) garbage collection event.

In the given example, any objects that have survived a certain number of cycles (in this case, 15 cycles) in the survivor space are moved by the algorithm to the old generation. The old generation is represented by a single section known as the tenured generation.

Metaspace

In Java 8 and later versions, the permanent generation was removed and replaced by a region called the Metaspace.

The Metaspace is where the JVM stores metadata representing the classes and methods of the application at runtime. Unlike the permanent generation, which was located in the heap and had a fixed size, the Metaspace is allocated out of the native memory of the JVM process and grows dynamically by default. However, it can be limited by setting the -XX:MaxMetaspaceSize parameter.

Occasionally, the JVM may perform specific operations to clean out the Metaspace, and when this occurs, it may trigger what is known as a major (full) garbage collection.

Stop The World?

A “stop the world” event sounds pretty dramatic, but think of it in terms of the Java application being the world.

During both minor garbage collection (young generation) and major garbage collection (Young + old + metaspace), a “stop the world” event occurs. This means that all application threads come to a complete halt and must wait until the garbage collection event finishes before they can resume their execution. The pausing of the application threads ensures the integrity and correctness of the garbage collection process.

--

--